mirror of https://github.com/sbt/sbt.git
Merge pull request #8303 from eed3si9n/wip/jdk25-main
[1.x] Support JDK 25 JEP-512/JEP-445 Main run
This commit is contained in:
commit
b52944cd3c
|
|
@ -21,8 +21,8 @@ jobs:
|
||||||
distribution: temurin
|
distribution: temurin
|
||||||
jobtype: 2
|
jobtype: 2
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
java: 21
|
java: 25
|
||||||
distribution: temurin
|
distribution: zulu
|
||||||
jobtype: 3
|
jobtype: 3
|
||||||
- os: ubuntu-latest
|
- os: ubuntu-latest
|
||||||
java: 21
|
java: 21
|
||||||
|
|
|
||||||
|
|
@ -14,7 +14,7 @@ import java.util.Locale
|
||||||
import scala.reflect.macros.blackbox
|
import scala.reflect.macros.blackbox
|
||||||
import scala.language.experimental.macros
|
import scala.language.experimental.macros
|
||||||
import scala.language.reflectiveCalls
|
import scala.language.reflectiveCalls
|
||||||
import scala.util.control.NonFatal
|
import scala.util.Properties
|
||||||
|
|
||||||
object Util {
|
object Util {
|
||||||
def makeList[T](size: Int, value: T): List[T] = List.fill(size)(value)
|
def makeList[T](size: Int, value: T): List[T] = List.fill(size)(value)
|
||||||
|
|
@ -88,17 +88,7 @@ object Util {
|
||||||
def reduceIntents[A1, A2](intents: PartialFunction[A1, A2]*): PartialFunction[A1, A2] =
|
def reduceIntents[A1, A2](intents: PartialFunction[A1, A2]*): PartialFunction[A1, A2] =
|
||||||
intents.toList.reduceLeft(_ orElse _)
|
intents.toList.reduceLeft(_ orElse _)
|
||||||
|
|
||||||
lazy val majorJavaVersion: Int =
|
lazy val isJava19Plus: Boolean = Properties.isJavaAtLeast("19")
|
||||||
try {
|
|
||||||
val javaVersion = sys.props.get("java.version").getOrElse("1.0")
|
|
||||||
if (javaVersion.startsWith("1.")) {
|
|
||||||
javaVersion.split("\\.")(1).toInt
|
|
||||||
} else {
|
|
||||||
javaVersion.split("\\.")(0).toInt
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
case NonFatal(_) => 0
|
|
||||||
}
|
|
||||||
|
|
||||||
private type GetId = {
|
private type GetId = {
|
||||||
def getId: Long
|
def getId: Long
|
||||||
|
|
@ -113,7 +103,7 @@ object Util {
|
||||||
* https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Thread.html#threadId()
|
* https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Thread.html#threadId()
|
||||||
*/
|
*/
|
||||||
def threadId: Long =
|
def threadId: Long =
|
||||||
if (majorJavaVersion < 19) {
|
if (!isJava19Plus) {
|
||||||
(Thread.currentThread(): AnyRef) match {
|
(Thread.currentThread(): AnyRef) match {
|
||||||
case g: GetId @unchecked => g.getId
|
case g: GetId @unchecked => g.getId
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -15,7 +15,7 @@ object Dependencies {
|
||||||
private val ioVersion = nightlyVersion.getOrElse("1.10.5")
|
private val ioVersion = nightlyVersion.getOrElse("1.10.5")
|
||||||
private val lmVersion =
|
private val lmVersion =
|
||||||
sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.11.5")
|
sys.props.get("sbt.build.lm.version").orElse(nightlyVersion).getOrElse("1.11.5")
|
||||||
val zincVersion = nightlyVersion.getOrElse("1.10.8")
|
val zincVersion = nightlyVersion.getOrElse("1.11.0")
|
||||||
|
|
||||||
private val sbtIO = "org.scala-sbt" %% "io" % ioVersion
|
private val sbtIO = "org.scala-sbt" %% "io" % ioVersion
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -19,7 +19,7 @@ import sbt.util.Logger
|
||||||
|
|
||||||
import scala.sys.process.Process
|
import scala.sys.process.Process
|
||||||
import scala.util.control.NonFatal
|
import scala.util.control.NonFatal
|
||||||
import scala.util.{ Failure, Success, Try }
|
import scala.util.{ Failure, Properties, Success, Try }
|
||||||
|
|
||||||
sealed trait ScalaRun {
|
sealed trait ScalaRun {
|
||||||
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Try[Unit]
|
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Try[Unit]
|
||||||
|
|
@ -81,7 +81,7 @@ class Run(private[sbt] val newLoader: Seq[File] => ClassLoader, trapExit: Boolea
|
||||||
def execute(): Unit =
|
def execute(): Unit =
|
||||||
try {
|
try {
|
||||||
log.debug(" Classpath:\n\t" + classpath.mkString("\n\t"))
|
log.debug(" Classpath:\n\t" + classpath.mkString("\n\t"))
|
||||||
val main = getMainMethod(mainClass, loader)
|
val main = detectMainMethod(mainClass, loader)
|
||||||
invokeMain(loader, main, options)
|
invokeMain(loader, main, options)
|
||||||
} catch {
|
} catch {
|
||||||
case e: java.lang.reflect.InvocationTargetException =>
|
case e: java.lang.reflect.InvocationTargetException =>
|
||||||
|
|
@ -125,14 +125,22 @@ class Run(private[sbt] val newLoader: Seq[File] => ClassLoader, trapExit: Boolea
|
||||||
}
|
}
|
||||||
private def invokeMain(
|
private def invokeMain(
|
||||||
loader: ClassLoader,
|
loader: ClassLoader,
|
||||||
main: Method,
|
main: DetectedMain,
|
||||||
options: Seq[String]
|
options: Seq[String]
|
||||||
): Unit = {
|
): Unit = {
|
||||||
val currentThread = Thread.currentThread
|
val currentThread = Thread.currentThread
|
||||||
val oldLoader = Thread.currentThread.getContextClassLoader
|
val oldLoader = Thread.currentThread.getContextClassLoader
|
||||||
currentThread.setContextClassLoader(loader)
|
currentThread.setContextClassLoader(loader)
|
||||||
try {
|
try {
|
||||||
main.invoke(null, options.toArray[String]); ()
|
if (main.isStatic) {
|
||||||
|
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]
|
||||||
|
if (main.parameterCount > 0) main.method.invoke(ref, options.toArray[String])
|
||||||
|
else main.method.invoke(ref)
|
||||||
|
}
|
||||||
|
()
|
||||||
} catch {
|
} catch {
|
||||||
case t: Throwable =>
|
case t: Throwable =>
|
||||||
t.getCause match {
|
t.getCause match {
|
||||||
|
|
@ -148,19 +156,39 @@ class Run(private[sbt] val newLoader: Seq[File] => ClassLoader, trapExit: Boolea
|
||||||
currentThread.setContextClassLoader(oldLoader)
|
currentThread.setContextClassLoader(oldLoader)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def getMainMethod(mainClassName: String, loader: ClassLoader) = {
|
def getMainMethod(mainClassName: String, loader: ClassLoader): Method =
|
||||||
|
detectMainMethod(mainClassName, loader).method
|
||||||
|
|
||||||
|
private def detectMainMethod(mainClassName: String, loader: ClassLoader) = {
|
||||||
val mainClass = Class.forName(mainClassName, true, loader)
|
val mainClass = Class.forName(mainClassName, true, loader)
|
||||||
val method = mainClass.getMethod("main", classOf[Array[String]])
|
if (Run.isJava25Plus) {
|
||||||
// jvm allows the actual main class to be non-public and to run a method in the non-public class,
|
val method = try {
|
||||||
// we need to make it accessible
|
mainClass.getMethod("main", classOf[Array[String]])
|
||||||
method.setAccessible(true)
|
} catch {
|
||||||
val modifiers = method.getModifiers
|
case _: NoSuchMethodException => mainClass.getMethod("main")
|
||||||
if (!isPublic(modifiers))
|
}
|
||||||
throw new NoSuchMethodException(mainClassName + ".main is not public")
|
method.setAccessible(true)
|
||||||
if (!isStatic(modifiers))
|
val modifiers = method.getModifiers
|
||||||
throw new NoSuchMethodException(mainClassName + ".main is not static")
|
DetectedMain(mainClass, method, isStatic(modifiers), method.getParameterCount())
|
||||||
method
|
} else {
|
||||||
|
val method = mainClass.getMethod("main", classOf[Array[String]])
|
||||||
|
// jvm allows the actual main class to be non-public and to run a method in the non-public class,
|
||||||
|
// we need to make it accessible
|
||||||
|
method.setAccessible(true)
|
||||||
|
val modifiers = method.getModifiers
|
||||||
|
if (!isPublic(modifiers))
|
||||||
|
throw new NoSuchMethodException(mainClassName + ".main is not public")
|
||||||
|
if (!isStatic(modifiers))
|
||||||
|
throw new NoSuchMethodException(mainClassName + ".main is not static")
|
||||||
|
DetectedMain(mainClass, method, isStatic = true, method.getParameterCount())
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
private case class DetectedMain(
|
||||||
|
mainClass: Class[?],
|
||||||
|
method: Method,
|
||||||
|
isStatic: Boolean,
|
||||||
|
parameterCount: Int
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
/** This module is an interface to starting the scala interpreter or runner.*/
|
/** This module is an interface to starting the scala interpreter or runner.*/
|
||||||
|
|
@ -195,4 +223,6 @@ object Run {
|
||||||
s"""nonzero exit code returned from $label: $exitCode""".stripMargin
|
s"""nonzero exit code returned from $label: $exitCode""".stripMargin
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
|
|
||||||
|
private[sbt] lazy val isJava25Plus: Boolean = Properties.isJavaAtLeast("25")
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,7 @@
|
||||||
|
package example
|
||||||
|
|
||||||
|
class A {
|
||||||
|
def main(): Unit = {
|
||||||
|
println("hi")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,11 @@
|
||||||
|
// 2.12.x uses Zinc's compiler bridge
|
||||||
|
ThisBuild / scalaVersion := "2.12.20"
|
||||||
|
|
||||||
|
@transient
|
||||||
|
lazy val check = taskKey[Unit]("")
|
||||||
|
|
||||||
|
check := {
|
||||||
|
if (scala.util.Properties.isJavaAtLeast("25"))
|
||||||
|
(Compile / run).toTask(" ").value
|
||||||
|
else ()
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,2 @@
|
||||||
|
# > run
|
||||||
|
> check
|
||||||
Loading…
Reference in New Issue