Merge branch '1.12.x' into wip/merge-1.12.x

This commit is contained in:
Eugene Yokota 2025-11-24 02:03:38 -05:00
commit 5730383340
24 changed files with 326 additions and 141 deletions

View File

@ -33,7 +33,9 @@ jobs:
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

View File

@ -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
@ -708,6 +708,7 @@ lazy val mainProj = (project in file("main"))
mimaSettings,
mimaBinaryIssueFilters ++= Vector(
exclude[DirectMissingMethodProblem]("sbt.Keys.scalaCompilerBridgeBinaryJar"),
exclude[DirectMissingMethodProblem]("sbt.internal.Compiler.*"),
),
)
.dependsOn(lmCore, lmIvy, lmCoursierShadedPublishing)

View File

@ -1,3 +1,3 @@
-XX:+CMSClassUnloadingEnabled
#-XX:+CMSClassUnloadingEnabled
#-XX:ReservedCodeCacheSize=192m
#-Duser.timezone=GMT

View File

@ -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 =

View File

@ -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%

View File

@ -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 (

View File

@ -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

View File

@ -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

View File

@ -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

View File

@ -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
@ -179,23 +190,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,

View File

@ -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(

View File

@ -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*) => () }
}

View File

@ -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]])

25
sbt
View File

@ -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

View File

@ -0,0 +1 @@
run / fork := true

View File

@ -0,0 +1,7 @@
package example;
class A {
void main() {
System.out.println("package private no args");
}
}

View File

@ -0,0 +1,7 @@
package example;
class A {
protected void main() {
System.out.println("protected no args");
}
}

View File

@ -0,0 +1,7 @@
package example;
class A {
void main(String[] args) {
System.out.println("package private with args");
}
}

View File

@ -0,0 +1,7 @@
package example;
class A {
private void main(String[] args) {
System.out.println("private");
}
}

View File

@ -0,0 +1,3 @@
void main() {
IO.println("compact source file with java.lang.IO");
}

View File

@ -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 ()
}
)

View File

@ -1,2 +0,0 @@
# > run
> check

View File

@ -0,0 +1,7 @@
# > run
> a1/check
> a2/check
> a3/check
> a4/check
-> a5/check
> a6/check