diff --git a/.github/workflows/ci.yml b/.github/workflows/ci.yml index 457caad2e..9a6dd074a 100644 --- a/.github/workflows/ci.yml +++ b/.github/workflows/ci.yml @@ -70,18 +70,18 @@ jobs: git config --global core.autocrlf false git config --global core.eol lf - name: Checkout sbt/sbt - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: fetch-depth: 0 submodules: true - name: Checkout sbt/io - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: sbt/io ref: develop path: io - name: Checkout sbt/zinc - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: sbt/zinc ref: develop diff --git a/.github/workflows/client-test.yml b/.github/workflows/client-test.yml index 8b1485a47..ea752c5a7 100644 --- a/.github/workflows/client-test.yml +++ b/.github/workflows/client-test.yml @@ -28,12 +28,14 @@ jobs: SBT_ETC_FILE: $HOME/etc/sbt/sbtopts TEST_SBT_VER: 1.11.4 steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Setup JDK uses: actions/setup-java@v5 with: distribution: "zulu" - java-version: "8" + java-version: | + 25 + 8 cache: sbt - uses: sbt/setup-sbt@v1 with: @@ -55,12 +57,13 @@ jobs: ./client/target/bin/sbtn --sbt-script=$(pwd)/sbt about ./client/target/bin/sbtn --sbt-script=$(pwd)/sbt shutdown # test launcher script - echo build using JDK 8 test using JDK 8 and JDK 11 + echo build using JDK 8 test using JDK 8 and JDK 25 cd launcher-package sbt -Dsbt.build.version=$TEST_SBT_VER rpm:packageBin debian:packageBin sbt -Dsbt.build.version=$TEST_SBT_VER integrationTest/test cd citest && ./test.sh - $HOME/bin/jabba install $JDK11 && exec $HOME/bin/jabba which --home $JDK11 + JAVA_HOME="$JAVA_HOME_25_X64" + PATH="$JAVA_HOME_25_X64/bin:$PATH" java -Xmx32m -version ./test.sh - name: Client test (macOS) @@ -93,7 +96,7 @@ jobs: ./client/target/bin/sbtn --sbt-script=$(pwd)/launcher-package/src/universal/bin/sbt.bat about ./client/target/bin/sbtn --sbt-script=$(pwd)/launcher-package/src/universal/bin/sbt.bat shutdown # test launcher script - echo build using JDK 8, test using JDK 8, on Windows + echo build using JDK 8 test using JDK 8 and JDK 25 cd launcher-package bin/coursier.bat resolve sbt -Dsbt.build.version=$TEST_SBT_VER integrationTest/test diff --git a/.github/workflows/dependency-graph.yml b/.github/workflows/dependency-graph.yml index cbad57297..5414f60eb 100644 --- a/.github/workflows/dependency-graph.yml +++ b/.github/workflows/dependency-graph.yml @@ -12,6 +12,6 @@ jobs: name: Submit Dependency Graph runs-on: ubuntu-latest # or windows-latest, or macOS-latest steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - uses: sbt/setup-sbt@v1 - uses: scalacenter/sbt-dependency-submission@v3 diff --git a/.github/workflows/nightly.yml b/.github/workflows/nightly.yml index 8db1967a4..034c53f59 100644 --- a/.github/workflows/nightly.yml +++ b/.github/workflows/nightly.yml @@ -21,15 +21,15 @@ jobs: JAVA_OPTS: -Xms800M -Xmx800M -Xss6M -XX:ReservedCodeCacheSize=128M -server -Dsbt.io.virtual=false -Dfile.encoding=UTF-8 steps: - name: Checkout sbt/sbt - uses: actions/checkout@v5 + uses: actions/checkout@v6 - name: Checkout sbt/io - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: sbt/io ref: develop path: io - name: Checkout sbt/zinc - uses: actions/checkout@v5 + uses: actions/checkout@v6 with: repository: sbt/zinc ref: develop diff --git a/.github/workflows/server-test.yml b/.github/workflows/server-test.yml index f5a0902c8..07871cb31 100644 --- a/.github/workflows/server-test.yml +++ b/.github/workflows/server-test.yml @@ -14,7 +14,7 @@ jobs: JVM_OPTS: -Xms800M -Xmx2G -Xss6M -XX:ReservedCodeCacheSize=128M -server -Dsbt.io.virtual=false -Dfile.encoding=UTF-8 SBT_ETC_FILE: $HOME/etc/sbt/sbtopts steps: - - uses: actions/checkout@v5 + - uses: actions/checkout@v6 - name: Setup JDK uses: actions/setup-java@v5 with: diff --git a/build.sbt b/build.sbt index 3afd3e9a4..1472deeba 100644 --- a/build.sbt +++ b/build.sbt @@ -47,7 +47,7 @@ ThisBuild / mimaFailOnNoPrevious := false 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 / semanticdbVersion := "4.14.1" val excludeLint = SettingKey[Set[Def.KeyedInitialize[?]]]("excludeLintKeys") Global / excludeLint := (Global / excludeLint).?.value.getOrElse(Set.empty) Global / excludeLint += Utils.componentID @@ -735,6 +735,7 @@ lazy val mainProj = (project in file("main")) exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCacheResolvers"), exclude[DirectMissingMethodProblem]("sbt.Keys.remoteCachePom"), exclude[DirectMissingMethodProblem]("sbt.internal.RemoteCache.*"), + exclude[DirectMissingMethodProblem]("sbt.internal.Compiler.*"), ), ) .dependsOn(lmCore, lmIvy, lmCoursierShadedPublishing) diff --git a/launcher-package/citest/test3/.jvmopts b/launcher-package/citest/test3/.jvmopts index ecacefc9a..a18306841 100644 --- a/launcher-package/citest/test3/.jvmopts +++ b/launcher-package/citest/test3/.jvmopts @@ -1,3 +1,3 @@ --XX:+CMSClassUnloadingEnabled +#-XX:+CMSClassUnloadingEnabled #-XX:ReservedCodeCacheSize=192m #-Duser.timezone=GMT \ No newline at end of file diff --git a/launcher-package/citest/test3/build.sbt b/launcher-package/citest/test3/build.sbt index 6cba9926c..dbe7fe7f9 100755 --- a/launcher-package/citest/test3/build.sbt +++ b/launcher-package/citest/test3/build.sbt @@ -27,19 +27,19 @@ lazy val root = (project in file(".")) assert(ys.size == 1, s"ys has more than one item: $ys") assert(ys(0) startsWith "Java HotSpot(TM) 64-Bit Server VM warning") }, - checkNumericVersion = { + checkNumericVersion := { val xs = IO.readLines(file("numericVersion.txt")).toVector val expectedVersion = "^"+versionRegEx+"$" assert(xs(0).matches(expectedVersion)) }, - checkScriptVersion = { + checkScriptVersion := { val xs = IO.readLines(file("scriptVersion.txt")).toVector val expectedVersion = "^"+versionRegEx+"$" assert(xs(0).matches(expectedVersion)) }, - checkVersion = { + checkVersion := { val out = IO.readLines(file("version.txt")).toVector.mkString("\n") val expectedVersion = diff --git a/launcher-package/citest/test3/test3.bat b/launcher-package/citest/test3/test3.bat index 38b1bde4f..52f489a0f 100644 --- a/launcher-package/citest/test3/test3.bat +++ b/launcher-package/citest/test3/test3.bat @@ -2,16 +2,18 @@ SETLOCAL -SET JAVA_HOME=C:\jdk11 -SET PATH=C:\jdk11\bin;%PATH% +SET JAVA_HOME=%JAVA_HOME_25_X64% +SET PATH=%JAVA_HOME_25_X64%\bin;%PATH% SET SBT_OPTS=-Xmx4g -Dfile.encoding=UTF8 SET BASE_DIR=%CD% SET SCRIPT_DIR=%~dp0 CD %SCRIPT_DIR% -"%BASE_DIR%freshly-baked\sbt\bin\sbt" about 1> output.txt 2> err.txt -"%BASE_DIR%freshly-baked\sbt\bin\sbt" check +"%BASE_DIR%\freshly-baked\sbt\bin\sbt" about 1> output.txt 2> err.txt +"%BASE_DIR%\freshly-baked\sbt\bin\sbt" check CD %BASE_DIR% ENDLOCAL + +IF %errorlevel% NEQ 0 EXIT /b %errorlevel% diff --git a/launcher-package/src/universal/bin/sbt.bat b/launcher-package/src/universal/bin/sbt.bat index 093c87c49..d18ce0b8a 100755 --- a/launcher-package/src/universal/bin/sbt.bat +++ b/launcher-package/src/universal/bin/sbt.bat @@ -39,6 +39,7 @@ set sbt_args_color= set sbt_args_no_colors= set sbt_args_no_global= set sbt_args_no_share= +set sbt_args_no_hide_jdk_warnings= set sbt_args_sbt_jar= set sbt_args_ivy= set sbt_args_supershell= @@ -231,6 +232,14 @@ if defined _no_server_arg ( goto args_loop ) +if "%~0" == "--no-hide-jdk-warnings" set _no_hide_jdk_warnings=true + +if defined _no_hide_jdk_warnings ( + set _no_hide_jdk_warnings= + set sbt_args_no_hide_jdk_warnings=1 + goto args_loop +) + if "%~0" == "-no-global" set _no_global_arg=true if "%~0" == "--no-global" set _no_global_arg=true @@ -677,6 +686,12 @@ if defined sbt_args_no_server ( set _SBT_OPTS=-Dsbt.io.virtual=false -Dsbt.server.autostart=false !_SBT_OPTS! ) +if not defined sbt_args_no_hide_jdk_warnings ( + if /I !JAVA_VERSION! EQU 25 ( + set _SBT_OPTS=--sun-misc-unsafe-memory-access=allow --enable-native-access=ALL-UNNAMED !_SBT_OPTS! + ) +) + rem TODO: _SBT_OPTS needs to be processed as args and diffed against SBT_ARGS if !sbt_args_print_sbt_version! equ 1 ( diff --git a/lm-core/src/main/scala/sbt/librarymanagement/ConfigurationExtra.scala b/lm-core/src/main/scala/sbt/librarymanagement/ConfigurationExtra.scala index 8aa8997e9..ff62ede16 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/ConfigurationExtra.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/ConfigurationExtra.scala @@ -47,6 +47,7 @@ object Configurations { lazy val ScalaTool = Configuration.of("ScalaTool", "scala-tool").hide lazy val ScalaDocTool = Configuration.of("ScalaDocTool", "scala-doc-tool").hide + lazy val ScalaReplTool = Configuration.of("ScalaReplTool", "scala-repl-tool").hide lazy val CompilerPlugin = Configuration.of("CompilerPlugin", "plugin").hide lazy val Component = Configuration.of("Component", "component").hide diff --git a/lm-core/src/main/scala/sbt/librarymanagement/ScalaArtifacts.scala b/lm-core/src/main/scala/sbt/librarymanagement/ScalaArtifacts.scala index 9fbaf1283..abd7eed5d 100644 --- a/lm-core/src/main/scala/sbt/librarymanagement/ScalaArtifacts.scala +++ b/lm-core/src/main/scala/sbt/librarymanagement/ScalaArtifacts.scala @@ -16,6 +16,7 @@ object ScalaArtifacts { final val ScaladocID = "scaladoc" final val Scala3DocID = "scala3doc" final val Scala3TastyInspectorID = "scala3-tasty-inspector" + final val Scala3ReplID = "scala3-repl" final val Scala3_8Artifacts = Vector(LibraryID, Scala3LibraryID) private[sbt] final val Scala3LibraryPrefix = Scala3LibraryID + "_" @@ -24,6 +25,7 @@ object ScalaArtifacts { private[sbt] final val ScaladocPrefix = ScaladocID + "_" private[sbt] final val Scala3DocPrefix = Scala3DocID + "_" private[sbt] final val Scala3TastyInspectorPrefix = Scala3TastyInspectorID + "_" + private[sbt] final val Scala3ReplPrefix = Scala3ReplID + "_" def isScala2Artifact(name: String): Boolean = { name == LibraryID || name == CompilerID || name == ReflectID || name == ActorsID || name == ScalapID @@ -35,11 +37,21 @@ object ScalaArtifacts { name == Scala3InterfacesID || name.startsWith(ScaladocPrefix) || name.startsWith(Scala3DocPrefix) || - name.startsWith(Scala3TastyInspectorPrefix) + name.startsWith(Scala3TastyInspectorPrefix) || + name.startsWith(Scala3ReplPrefix) } def isScala3(scalaVersion: String): Boolean = scalaVersion.startsWith("3.") + /** + * Returns true for pre-release nightlies intentionally. + */ + def isScala3_8Plus(scalaVersion: String): Boolean = + isScala3(scalaVersion) && (scalaVersion match { + case VersionNumber(numbers, _, _) if numbers.size > 2 && numbers(1) >= 8 => true + case _ => false + }) + private[sbt] def isScala3M123(scalaVersion: String): Boolean = (scalaVersion == "3.0.0-M1") || (scalaVersion == "3.0.0-M2") || @@ -65,6 +77,18 @@ object ScalaArtifacts { .platform(Platform.jvm) else ModuleID(org, LibraryID, version).platform(Platform.jvm) + private[sbt] def replToolDependencies( + org: String, + version: String + ): Seq[ModuleID] = + if (isScala3_8Plus(version)) + Seq( + ModuleID(org, Scala3ReplID, version) + .withConfigurations(Some(Configurations.ScalaReplTool.name + "->default(compile)")) + .withCrossVersion(CrossVersion.binary) + ) + else Seq.empty + private[sbt] def docToolDependencies( org: String, version: String diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index a2e89a311..87ee441c4 100644 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -671,100 +671,105 @@ object Defaults extends BuildCommon { ) // This is included into JvmPlugin.projectSettings - def compileBase = inTask(console)(compilersSetting :: Nil) ++ compileBaseGlobal ++ Seq( - useScalaReplJLine :== false, - scalaInstanceTopLoader := { - val topLoader = if (!useScalaReplJLine.value) { - // the JLineLoader contains the SbtInterfaceClassLoader - classOf[org.jline.terminal.Terminal].getClassLoader - } else classOf[Compilers].getClassLoader // the SbtInterfaceClassLoader - - // Scala 2.10 shades jline in the console so we need to make sure that it loads a compatible - // jansi version. Because of the shading, console does not work with the thin client for 2.10.x. - if (scalaVersion.value.startsWith("2.10.")) new ClassLoader(topLoader) { - override protected def loadClass(name: String, resolve: Boolean): Class[?] = { - if (name.startsWith("org.fusesource")) throw new ClassNotFoundException(name) - super.loadClass(name, resolve) + def compileBase = + inTask(console)( + Seq( + scalaInstance := Compiler.scalaInstanceTask(Some(Configurations.ScalaReplTool)).value, + ) ++ compilersSetting + ) ++ compileBaseGlobal ++ Seq( + useScalaReplJLine :== false, + scalaInstanceTopLoader := { + val topLoader = if (!useScalaReplJLine.value) { + // the JLineLoader contains the SbtInterfaceClassLoader + classOf[org.jline.terminal.Terminal].getClassLoader + } else classOf[Compilers].getClassLoader // the SbtInterfaceClassLoader + // Scala 2.10 shades jline in the console so we need to make sure that it loads a compatible + // jansi version. Because of the shading, console does not work with the thin client for 2.10.x. + if (scalaVersion.value.startsWith("2.10.")) new ClassLoader(topLoader) { + override protected def loadClass(name: String, resolve: Boolean): Class[?] = { + if (name.startsWith("org.fusesource")) throw new ClassNotFoundException(name) + super.loadClass(name, resolve) + } } - } - else topLoader - }, - scalaInstance := Def.uncached(Compiler.scalaInstanceTask.value), - crossVersion := (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled), - pluginCrossBuild / sbtBinaryVersion := binarySbtVersion( - (pluginCrossBuild / sbtVersion).value - ), - // Use (pluginCrossBuild / sbtVersion) to pick the sbt module to depend from the plugin. - // Because `pluginCrossBuild / sbtVersion` can be scoped to project level, - // this setting needs to be set here too. - pluginCrossBuild / sbtDependency := { - val app = appConfiguration.value - val id = app.provider.id - val sv = (pluginCrossBuild / sbtVersion).value - val scalaV = (pluginCrossBuild / scalaVersion).value - val binVersion = (pluginCrossBuild / scalaBinaryVersion).value - val cross = id.crossVersionedValue match { - case CrossValue.Disabled => Disabled() - case CrossValue.Full => CrossVersion.full - case CrossValue.Binary => CrossVersion.binary - } - val base = ModuleID(id.groupID, id.name, sv).withCrossVersion(cross).platform(Platform.jvm) - CrossVersion(scalaV, binVersion)(base).withCrossVersion(Disabled()) - }, - 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 - }, - scalaCompilerBridgeBin := Def - .ifS(Def.task { - val sv = scalaVersion.value - val managed = managedScalaInstance.value - val hasSbtBridge = ScalaArtifacts.isScala3(sv) || ZincLmUtil.hasScala2SbtBridge(sv) - hasSbtBridge && managed - })(Def.cachedTask { - val sv = scalaVersion.value - val conv = fileConverter.value - val s = streams.value - val t = target.value - val r = dependencyResolution.value - val uc = updateConfiguration.value - val jar = ZincLmUtil.fetchDefaultBridgeModule( - sv, - r, - uc, - (update / unresolvedWarningConfiguration).value, - s.log - ) - val out = t / "compiler-bridge" / jar.getName() - val outVf = conv.toVirtualFile(out.toPath()) - IO.copyFile(jar, out) - Def.declareOutput(outVf) - Vector(outVf: HashedVirtualFileRef) - })(Def.task(Vector.empty)) - .value, - scalaCompilerBridgeSource := ZincLmUtil.getDefaultBridgeSourceModule(scalaVersion.value), - auxiliaryClassFiles ++= { - if (ScalaArtifacts.isScala3(scalaVersion.value)) List(TastyFiles.instance) - else Nil - }, - consoleProject / scalaCompilerBridgeSource := ZincLmUtil.getDefaultBridgeSourceModule( - appConfiguration.value.provider.scalaProvider.version - ), - classpathOptions := ClasspathOptionsUtil.noboot(scalaVersion.value), - console / classpathOptions := ClasspathOptionsUtil.replNoboot(scalaVersion.value), - ) + else topLoader + }, + scalaInstance := Def.uncached(Compiler.scalaInstanceTask(None).value), + crossVersion := (if (crossPaths.value) CrossVersion.binary else CrossVersion.disabled), + pluginCrossBuild / sbtBinaryVersion := binarySbtVersion( + (pluginCrossBuild / sbtVersion).value + ), + // Use (pluginCrossBuild / sbtVersion) to pick the sbt module to depend from the plugin. + // Because `pluginCrossBuild / sbtVersion` can be scoped to project level, + // this setting needs to be set here too. + pluginCrossBuild / sbtDependency := { + val app = appConfiguration.value + val id = app.provider.id + val sv = (pluginCrossBuild / sbtVersion).value + val scalaV = (pluginCrossBuild / scalaVersion).value + val binVersion = (pluginCrossBuild / scalaBinaryVersion).value + val cross = id.crossVersionedValue match { + case CrossValue.Disabled => Disabled() + case CrossValue.Full => CrossVersion.full + case CrossValue.Binary => CrossVersion.binary + } + val base = ModuleID(id.groupID, id.name, sv).withCrossVersion(cross).platform(Platform.jvm) + CrossVersion(scalaV, binVersion)(base).withCrossVersion(Disabled()) + }, + 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 + }, + scalaCompilerBridgeBin := Def + .ifS(Def.task { + val sv = scalaVersion.value + val managed = managedScalaInstance.value + val hasSbtBridge = ScalaArtifacts.isScala3(sv) || ZincLmUtil.hasScala2SbtBridge(sv) + hasSbtBridge && managed + })(Def.cachedTask { + val sv = scalaVersion.value + val conv = fileConverter.value + val s = streams.value + val t = target.value + val r = dependencyResolution.value + val uc = updateConfiguration.value + val jar = ZincLmUtil.fetchDefaultBridgeModule( + sv, + r, + uc, + (update / unresolvedWarningConfiguration).value, + s.log + ) + val out = t / "compiler-bridge" / jar.getName() + val outVf = conv.toVirtualFile(out.toPath()) + IO.copyFile(jar, out) + Def.declareOutput(outVf) + Vector(outVf: HashedVirtualFileRef) + })(Def.task(Vector.empty)) + .value, + scalaCompilerBridgeSource := ZincLmUtil.getDefaultBridgeSourceModule(scalaVersion.value), + auxiliaryClassFiles ++= { + if (ScalaArtifacts.isScala3(scalaVersion.value)) List(TastyFiles.instance) + else Nil + }, + consoleProject / scalaCompilerBridgeSource := ZincLmUtil.getDefaultBridgeSourceModule( + appConfiguration.value.provider.scalaProvider.version + ), + classpathOptions := ClasspathOptionsUtil.noboot(scalaVersion.value), + console / classpathOptions := ClasspathOptionsUtil.replNoboot(scalaVersion.value), + ) + private lazy val compileBaseGlobal: Seq[Setting[?]] = globalDefaults( Seq( auxiliaryClassFiles :== Nil, @@ -1935,6 +1940,7 @@ object Defaults extends BuildCommon { def docTaskSettings(key: TaskKey[File] = doc): Seq[Setting[?]] = inTask(key)( Seq( + scalaInstance := Compiler.scalaInstanceTask(Some(Configurations.ScalaDocTool)).value, apiMappings ++= { val dependencyCp = dependencyClasspath.value val log = streams.value.log @@ -2017,7 +2023,7 @@ object Defaults extends BuildCommon { } out } - ) + ) ++ compilersSetting ) def discoverMainClasses(analysis: CompileAnalysis): Seq[String] = analysis match { @@ -3127,7 +3133,7 @@ object Classpaths { ivyConfigurations ++= Configurations.auxiliary, ivyConfigurations ++= { if (managedScalaInstance.value && scalaHome.value.isEmpty) - Configurations.ScalaTool :: Configurations.ScalaDocTool :: Nil + Configurations.ScalaTool :: Configurations.ScalaDocTool :: Configurations.ScalaReplTool :: Nil else Nil }, // Coursier needs these @@ -3351,15 +3357,16 @@ object Classpaths { val pluginAdjust = if (isPlugin) sbtdeps +: base else base - val sbtOrg = scalaOrganization.value + val scalaOrg = scalaOrganization.value val version = scalaVersion.value val extResolvers = externalResolvers.value val allToolDeps = if scalaHome.value.isDefined || scalaModuleInfo.value.isEmpty || !managedScalaInstance.value then Nil else - ScalaArtifacts.toolDependencies(sbtOrg, version) ++ - ScalaArtifacts.docToolDependencies(sbtOrg, version) + ScalaArtifacts.toolDependencies(scalaOrg, version) ++ + ScalaArtifacts.docToolDependencies(scalaOrg, version) ++ + ScalaArtifacts.replToolDependencies(scalaOrg, version) allToolDeps.map(_.platform(Platform.jvm)) ++ pluginAdjust }, // in case of meta build, exclude all sbt modules from the dependency graph, so we can use the sbt resolved by the launcher diff --git a/main/src/main/scala/sbt/internal/Compiler.scala b/main/src/main/scala/sbt/internal/Compiler.scala index 50ab721e0..be4d02143 100644 --- a/main/src/main/scala/sbt/internal/Compiler.scala +++ b/main/src/main/scala/sbt/internal/Compiler.scala @@ -1,3 +1,11 @@ +/* + * 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 package internal @@ -5,6 +13,7 @@ import java.io.File import sbt.internal.inc.ScalaInstance import sbt.librarymanagement.{ Artifact, + Configuration, Configurations, ConfigurationReport, ScalaArtifacts, @@ -14,7 +23,7 @@ import sbt.librarymanagement.{ import xsbti.ScalaProvider object Compiler: - def scalaInstanceTask: Def.Initialize[Task[ScalaInstance]] = + def scalaInstanceTask(extraToolConf: Option[Configuration]): Def.Initialize[Task[ScalaInstance]] = Def.taskDyn { val sh = Keys.scalaHome.value val app = Keys.appConfiguration.value @@ -24,7 +33,7 @@ object Compiler: case _ => val scalaProvider = app.provider.scalaProvider if !managed then emptyScalaInstance - else scalaInstanceFromUpdate + else scalaInstanceFromUpdate(extraToolConf) } /** @@ -84,7 +93,9 @@ object Compiler: ) } - def scalaInstanceFromUpdate: Def.Initialize[Task[ScalaInstance]] = Def.task { + def scalaInstanceFromUpdate( + extraToolConf: Option[Configuration] + ): Def.Initialize[Task[ScalaInstance]] = Def.task { val sv = Keys.scalaVersion.value val fullReport = Keys.update.value val s = Keys.streams.value @@ -147,13 +158,11 @@ object Compiler: else s.log.warn(msg) else () else () - def file(id: String): File = { - val files = for { - m <- toolReport.modules if m.module.name.startsWith(id) - (art, file) <- m.artifacts if art.`type` == Artifact.DefaultType - } yield file - files.headOption getOrElse sys.error(s"Missing $id jar file") - } + def file(id: String): Option[File] = + for + m <- toolReport.modules.find(_.module.name.startsWith(id)) + (_, file) <- m.artifacts.find(_._1.`type` == Artifact.DefaultType) + yield file val allCompilerJars = toolReport.modules.flatMap(_.artifacts.map(_._2)) val allDocJars = @@ -163,7 +172,7 @@ object Compiler: .toSeq .flatMap(_.modules) .flatMap(_.artifacts.map(_._2)) - val libraryJars = ScalaArtifacts.libraryIds(sv).map(file) + val libraryJars = ScalaArtifacts.libraryIds(sv).flatMap(file) makeScalaInstance( sv, @@ -179,23 +188,23 @@ object Compiler: version: String, libraryJars: Array[File], allCompilerJars: Seq[File], - allDocJars: Seq[File], + extraToolJars: Seq[File], state: State, topLoader: ClassLoader, ): ScalaInstance = val classLoaderCache = State.StateOpsImpl(state).extendedClassLoaderCache val compilerJars = allCompilerJars.filterNot(libraryJars.contains).distinct.toArray - val docJars = allDocJars + val toolJars = extraToolJars .filterNot(jar => libraryJars.contains(jar) || compilerJars.contains(jar)) .distinct .toArray - val allJars = libraryJars ++ compilerJars ++ docJars + val allJars = libraryJars ++ compilerJars ++ toolJars val libraryLoader = classLoaderCache(libraryJars.toList, topLoader) val compilerLoader = classLoaderCache(compilerJars.toList, libraryLoader) val fullLoader = - if (docJars.isEmpty) compilerLoader - else classLoaderCache(docJars.distinct.toList, compilerLoader) + if toolJars.isEmpty then compilerLoader + else classLoaderCache(toolJars.distinct.toList, compilerLoader) new ScalaInstance( version = version, loader = fullLoader, diff --git a/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala b/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala index bd6a7b264..b77be0492 100644 --- a/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala +++ b/main/src/main/scala/sbt/plugins/SemanticdbPlugin.scala @@ -29,7 +29,7 @@ object SemanticdbPlugin extends AutoPlugin { semanticdbEnabled := SysProp.semanticdb, semanticdbIncludeInJar := false, semanticdbOptions := List(), - semanticdbVersion := "4.9.9" + semanticdbVersion := "4.14.1" ) override lazy val projectSettings: Seq[Def.Setting[?]] = Seq( diff --git a/main/src/test/resources/xsource3/1.sbt.txt b/main/src/test/resources/xsource3/1.sbt.txt new file mode 100644 index 000000000..d37ff7c3a --- /dev/null +++ b/main/src/test/resources/xsource3/1.sbt.txt @@ -0,0 +1,7 @@ +ThisBuild / scalaVersion := "3.3.4" + +def foo(): Unit = { + List("") match { case List(path@_*) => () } + List("") match { case List(path *) => () } + List("") match { case List(path*) => () } +} diff --git a/run/src/main/scala/sbt/Run.scala b/run/src/main/scala/sbt/Run.scala index e867ec0ca..895b675ab 100644 --- a/run/src/main/scala/sbt/Run.scala +++ b/run/src/main/scala/sbt/Run.scala @@ -11,7 +11,7 @@ package sbt import java.io.File import java.nio.file.{ Path as NioPath } import java.lang.reflect.Method -import java.lang.reflect.Modifier.{ isPublic, isStatic } +import java.lang.reflect.Modifier.{ isPrivate, isPublic, isStatic } import sbt.internal.inc.ScalaInstance import sbt.internal.inc.classpath.{ ClasspathFilter, ClasspathUtil } import sbt.internal.util.MessageOnlyException @@ -149,10 +149,17 @@ class Run(private[sbt] val newLoader: Seq[NioPath] => ClassLoader, trapExit: Boo currentThread.setContextClassLoader(loader) try { if (main.isStatic) { + if (Run.isJava25Plus) { + main.method.setAccessible(true) + } if (main.parameterCount > 0) main.method.invoke(null, options.toArray[String]) else main.method.invoke(null) } else { - val ref = main.mainClass.getDeclaredConstructor().newInstance().asInstanceOf[AnyRef] + val constructor = main.mainClass.getDeclaredConstructor() + if (Run.isJava25Plus) { + constructor.setAccessible(true) + } + val ref = constructor.newInstance().asInstanceOf[AnyRef] if (main.parameterCount > 0) main.method.invoke(ref, options.toArray[String]) else main.method.invoke(ref) } @@ -182,10 +189,24 @@ class Run(private[sbt] val newLoader: Seq[NioPath] => ClassLoader, trapExit: Boo try { mainClass.getMethod("main", classOf[Array[String]]) } catch { - case _: NoSuchMethodException => mainClass.getMethod("main") + case _: NoSuchMethodException => + try { + mainClass.getMethod("main") + } catch { + case _: NoSuchMethodException => + try { + mainClass.getDeclaredMethod("main", classOf[Array[String]]) + } catch { + case _: NoSuchMethodException => + mainClass.getDeclaredMethod("main") + } + } } - method.setAccessible(true) val modifiers = method.getModifiers + if (isPrivate(modifiers)) { + throw new NoSuchMethodException(s"${mainClassName}.main is private") + } + method.setAccessible(true) DetectedMain(mainClass, method, isStatic(modifiers), method.getParameterCount()) } else { val method = mainClass.getMethod("main", classOf[Array[String]]) diff --git a/sbt b/sbt index 44d563b28..12dee0e5d 100755 --- a/sbt +++ b/sbt @@ -1,7 +1,7 @@ #!/usr/bin/env bash set +e -declare builtin_sbt_version="1.11.6" +declare builtin_sbt_version="1.11.7" declare -a residual_args declare -a java_args declare -a scalac_args @@ -28,6 +28,7 @@ declare sbtn_command="$SBTN_CMD" declare sbtn_version="1.11.6" declare use_colors=1 declare is_this_dir_sbt="" +declare hide_jdk_warnings=1 ### ------------------------------- ### ### Helper methods for BASH scripts ### @@ -85,7 +86,7 @@ CYGWIN_FLAG=$(if is_cygwin; then echo true; else echo false; fi) # windows style paths. cygwinpath() { local file="$1" - if [[ "$CYGWIN_FLAG" == "true" ]]; then #" + if [[ "$CYGWIN_FLAG" == "true" ]]; then echo $(cygpath -w $file) else echo $file @@ -114,7 +115,7 @@ echoerr_error () { echoerr -e "[${RED}error${NC}] $@" else echoerr "[error] $@" - fi + fi #" } vlog () { [[ $sbt_verbose || $sbt_debug ]] && echoerr "$@" @@ -350,6 +351,18 @@ addSbtScriptProperty () { fi } +addJdkWorkaround () { + local is_25="$(expr $java_version "=" 25)" + if [[ "$hide_jdk_warnings" == "0" ]]; then + : + else + if [[ "$is_25" == "1" ]]; then + addJava "--sun-misc-unsafe-memory-access=allow" + addJava "--enable-native-access=ALL-UNNAMED" + fi + fi +} + require_arg () { local type="$1" local opt="$2" @@ -535,7 +548,7 @@ run() { copyRt # If we're in cygwin, we should use the windows config, and terminal hacks - if [[ "$CYGWIN_FLAG" == "true" ]]; then #" + if [[ "$CYGWIN_FLAG" == "true" ]]; then stty -icanon min 1 -echo > /dev/null 2>&1 addJava "-Djline.terminal=jline.UnixTerminal" addJava "-Dsbt.cygwin=true" @@ -575,7 +588,7 @@ run() { exit_code=$? # Clean up the terminal from cygwin hacks. - if [[ "$CYGWIN_FLAG" == "true" ]]; then #" + if [[ "$CYGWIN_FLAG" == "true" ]]; then stty icanon echo > /dev/null 2>&1 fi exit $exit_code @@ -708,6 +721,7 @@ process_args () { -client|--client) use_sbtn=1 && shift ;; --server) use_sbtn=0 && shift ;; --jvm-client) use_sbtn=0 && use_jvm_client=1 && addSbt "--client" && shift ;; + --no-hide-jdk-warnings) hide_jdk_warnings=0 && shift ;; -mem|--mem) require_arg integer "$1" "$2" && addMemory "$2" && shift 2 ;; -jvm-debug|--jvm-debug) require_arg port "$1" "$2" && addDebugger $2 && shift 2 ;; @@ -870,6 +884,7 @@ else vlog "[process_args] java_version = '$java_version'" addDefaultMemory addSbtScriptProperty + addJdkWorkaround set -- "${residual_args[@]}" argumentCount=$# run diff --git a/sbt-app/src/sbt-test/run/daemon-exit/build.sbt b/sbt-app/src/sbt-test/run/daemon-exit/build.sbt new file mode 100644 index 000000000..6dbf94cfe --- /dev/null +++ b/sbt-app/src/sbt-test/run/daemon-exit/build.sbt @@ -0,0 +1 @@ +run / fork := true diff --git a/sbt-app/src/sbt-test/run/jep-512/A.scala b/sbt-app/src/sbt-test/run/jep-512/a1/A.scala similarity index 100% rename from sbt-app/src/sbt-test/run/jep-512/A.scala rename to sbt-app/src/sbt-test/run/jep-512/a1/A.scala diff --git a/sbt-app/src/sbt-test/run/jep-512/a2/A.java b/sbt-app/src/sbt-test/run/jep-512/a2/A.java new file mode 100644 index 000000000..cb51194e0 --- /dev/null +++ b/sbt-app/src/sbt-test/run/jep-512/a2/A.java @@ -0,0 +1,7 @@ +package example; + +class A { + void main() { + System.out.println("package private no args"); + } +} diff --git a/sbt-app/src/sbt-test/run/jep-512/a3/A.java b/sbt-app/src/sbt-test/run/jep-512/a3/A.java new file mode 100644 index 000000000..c1d2792cb --- /dev/null +++ b/sbt-app/src/sbt-test/run/jep-512/a3/A.java @@ -0,0 +1,7 @@ +package example; + +class A { + protected void main() { + System.out.println("protected no args"); + } +} diff --git a/sbt-app/src/sbt-test/run/jep-512/a4/A.java b/sbt-app/src/sbt-test/run/jep-512/a4/A.java new file mode 100644 index 000000000..6b646c14f --- /dev/null +++ b/sbt-app/src/sbt-test/run/jep-512/a4/A.java @@ -0,0 +1,7 @@ +package example; + +class A { + void main(String[] args) { + System.out.println("package private with args"); + } +} diff --git a/sbt-app/src/sbt-test/run/jep-512/a5/A.java b/sbt-app/src/sbt-test/run/jep-512/a5/A.java new file mode 100644 index 000000000..bb441141a --- /dev/null +++ b/sbt-app/src/sbt-test/run/jep-512/a5/A.java @@ -0,0 +1,7 @@ +package example; + +class A { + private void main(String[] args) { + System.out.println("private"); + } +} diff --git a/sbt-app/src/sbt-test/run/jep-512/a6/A.java b/sbt-app/src/sbt-test/run/jep-512/a6/A.java new file mode 100644 index 000000000..7ce4a32e7 --- /dev/null +++ b/sbt-app/src/sbt-test/run/jep-512/a6/A.java @@ -0,0 +1,3 @@ +void main() { + IO.println("compact source file with java.lang.IO"); +} diff --git a/sbt-app/src/sbt-test/run/jep-512/build.sbt b/sbt-app/src/sbt-test/run/jep-512/build.sbt index cfb7fd4c6..3adcc19ef 100644 --- a/sbt-app/src/sbt-test/run/jep-512/build.sbt +++ b/sbt-app/src/sbt-test/run/jep-512/build.sbt @@ -1,13 +1,54 @@ -// 2.12.x uses Zinc's compiler bridge -ThisBuild / scalaVersion := "2.12.20" - @transient lazy val check = taskKey[Unit]("") -Compile / run / fork := false +lazy val common = Def.settings( + // 2.12.x uses Zinc's compiler bridge + scalaVersion := "2.12.20", +) -check := { - if (scala.util.Properties.isJavaAtLeast("25")) - (Compile / fgRun).toTask(" ").value - else () -} +// use `runMain` instead of `run` because discoveredMainClasses return empty +// if JEP-512 java main method +// TODO fix zinc +// https://github.com/sbt/sbt/issues/7384#issuecomment-3361020003 +lazy val commonRunMainCheck = Def.settings( + check := { + if (scala.util.Properties.isJavaAtLeast("25")) { + (Compile / runMain).toTask(" example.A").value + } else () + } +) + +lazy val a1 = project + .settings(common) + .settings( + check := { + if (scala.util.Properties.isJavaAtLeast("25")) { + assert((Compile / discoveredMainClasses).value.size == 1) + (Compile / run).toTask(" ").value + } else () + } + ) + +lazy val a2 = project.settings(common, commonRunMainCheck) +lazy val a3 = project.settings(common, commonRunMainCheck) +lazy val a4 = project.settings(common, commonRunMainCheck) + +lazy val a5 = project.settings( + common, + check := { + if (scala.util.Properties.isJavaAtLeast("25")) { + (Compile / runMain).toTask(" example.A").value + } else { + sys.error("not jdk 25") + } + } +) + +lazy val a6 = project.settings( + common, + check := { + if (scala.util.Properties.isJavaAtLeast("25")) { + (Compile / runMain).toTask(" A").value + } else () + } +) diff --git a/sbt-app/src/sbt-test/run/jep-512/pending b/sbt-app/src/sbt-test/run/jep-512/pending deleted file mode 100644 index c28fe4985..000000000 --- a/sbt-app/src/sbt-test/run/jep-512/pending +++ /dev/null @@ -1,2 +0,0 @@ -# > run -> check diff --git a/sbt-app/src/sbt-test/run/jep-512/test b/sbt-app/src/sbt-test/run/jep-512/test new file mode 100644 index 000000000..9fbd123fa --- /dev/null +++ b/sbt-app/src/sbt-test/run/jep-512/test @@ -0,0 +1,7 @@ +# > run +> a1/check +> a2/check +> a3/check +> a4/check +-> a5/check +> a6/check