Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 2 additions & 2 deletions .github/workflows/ci.yml
Original file line number Diff line number Diff line change
Expand Up @@ -21,8 +21,8 @@ jobs:
distribution: temurin
jobtype: 2
- os: ubuntu-latest
java: 21
distribution: temurin
java: 25
distribution: zulu
jobtype: 3
- os: ubuntu-latest
java: 21
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,7 @@ import java.util.Locale
import scala.reflect.macros.blackbox
import scala.language.experimental.macros
import scala.language.reflectiveCalls
import scala.util.control.NonFatal
import scala.util.Properties

object Util {
def makeList[T](size: Int, value: T): List[T] = List.fill(size)(value)
Expand Down Expand Up @@ -88,17 +88,7 @@ object Util {
def reduceIntents[A1, A2](intents: PartialFunction[A1, A2]*): PartialFunction[A1, A2] =
intents.toList.reduceLeft(_ orElse _)

lazy val majorJavaVersion: Int =
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
}
lazy val isJava19Plus: Boolean = Properties.isJavaAtLeast("19")

private type GetId = {
def getId: Long
Expand All @@ -113,7 +103,7 @@ object Util {
* https://docs.oracle.com/en/java/javase/21/docs/api/java.base/java/lang/Thread.html#threadId()
*/
def threadId: Long =
if (majorJavaVersion < 19) {
if (!isJava19Plus) {
(Thread.currentThread(): AnyRef) match {
case g: GetId @unchecked => g.getId
}
Expand Down
2 changes: 1 addition & 1 deletion project/Dependencies.scala
Original file line number Diff line number Diff line change
Expand Up @@ -15,7 +15,7 @@ object Dependencies {
private val ioVersion = nightlyVersion.getOrElse("1.10.5")
private val lmVersion =
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

Expand Down
60 changes: 45 additions & 15 deletions run/src/main/scala/sbt/Run.scala
Original file line number Diff line number Diff line change
Expand Up @@ -19,7 +19,7 @@ import sbt.util.Logger

import scala.sys.process.Process
import scala.util.control.NonFatal
import scala.util.{ Failure, Success, Try }
import scala.util.{ Failure, Properties, Success, Try }

sealed trait ScalaRun {
def run(mainClass: String, classpath: Seq[File], options: Seq[String], log: Logger): Try[Unit]
Expand Down Expand Up @@ -81,7 +81,7 @@ class Run(private[sbt] val newLoader: Seq[File] => ClassLoader, trapExit: Boolea
def execute(): Unit =
try {
log.debug(" Classpath:\n\t" + classpath.mkString("\n\t"))
val main = getMainMethod(mainClass, loader)
val main = detectMainMethod(mainClass, loader)
invokeMain(loader, main, options)
} catch {
case e: java.lang.reflect.InvocationTargetException =>
Expand Down Expand Up @@ -125,14 +125,22 @@ class Run(private[sbt] val newLoader: Seq[File] => ClassLoader, trapExit: Boolea
}
private def invokeMain(
loader: ClassLoader,
main: Method,
main: DetectedMain,
options: Seq[String]
): Unit = {
val currentThread = Thread.currentThread
val oldLoader = Thread.currentThread.getContextClassLoader
currentThread.setContextClassLoader(loader)
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 {
case t: Throwable =>
t.getCause match {
Expand All @@ -148,19 +156,39 @@ class Run(private[sbt] val newLoader: Seq[File] => ClassLoader, trapExit: Boolea
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 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")
method
if (Run.isJava25Plus) {
val method = try {
mainClass.getMethod("main", classOf[Array[String]])
} catch {
case _: NoSuchMethodException => mainClass.getMethod("main")
}
method.setAccessible(true)
val modifiers = method.getModifiers
DetectedMain(mainClass, method, isStatic(modifiers), method.getParameterCount())
} 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.*/
Expand Down Expand Up @@ -195,4 +223,6 @@ object Run {
s"""nonzero exit code returned from $label: $exitCode""".stripMargin
)
)

private[sbt] lazy val isJava25Plus: Boolean = Properties.isJavaAtLeast("25")
}
7 changes: 7 additions & 0 deletions sbt-app/src/sbt-test/run/jep-512/A.scala
Original file line number Diff line number Diff line change
@@ -0,0 +1,7 @@
package example

class A {
def main(): Unit = {
println("hi")
}
}
11 changes: 11 additions & 0 deletions sbt-app/src/sbt-test/run/jep-512/build.sbt
Original file line number Diff line number Diff line change
@@ -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 ()
}
2 changes: 2 additions & 0 deletions sbt-app/src/sbt-test/run/jep-512/test
Original file line number Diff line number Diff line change
@@ -0,0 +1,2 @@
# > run
> check
Loading