diff --git a/build.sbt b/build.sbt index a7cc36c6b..fc5fc148a 100644 --- a/build.sbt +++ b/build.sbt @@ -10,7 +10,7 @@ import com.eed3si9n.jarjarabrams.ModuleCoordinate // ThisBuild settings take lower precedence, // but can be shared across the multi projects. ThisBuild / version := { - val v = "2.0.0-RC4-bin-SNAPSHOT" + val v = "2.0.0-RC6-bin-SNAPSHOT" nightlyVersion.getOrElse(v) } ThisBuild / Utils.version2_13 := "2.0.0-SNAPSHOT" @@ -303,7 +303,10 @@ lazy val utilCore = project utilCommonSettings, name := "Util Core", Utils.keywordsSettings, - mimaSettings + mimaSettings, + mimaBinaryIssueFilters ++= Seq( + exclude[DirectMissingMethodProblem]("sbt.internal.util.Util.majorJavaVersion"), + ), ) lazy val utilLogging = project diff --git a/internal/util-core/src/main/scala/sbt/internal/util/Util.scala b/internal/util-core/src/main/scala/sbt/internal/util/Util.scala index fd35934e0..3b241b27e 100644 --- a/internal/util-core/src/main/scala/sbt/internal/util/Util.scala +++ b/internal/util-core/src/main/scala/sbt/internal/util/Util.scala @@ -13,7 +13,7 @@ import java.util.Locale import scala.collection.concurrent.TrieMap import scala.reflect.Selectable.reflectiveSelectable -import scala.util.control.NonFatal +import scala.util.Properties object Util: def makeList[T](size: Int, value: T): List[T] = List.fill(size)(value) @@ -96,17 +96,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 @@ -121,13 +111,10 @@ 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) { - (Thread.currentThread(): AnyRef) match { + if !isJava19Plus then + (Thread.currentThread(): AnyRef) match case g: GetId @unchecked => g.getId - } - } else { - (Thread.currentThread(): AnyRef) match { + else + (Thread.currentThread(): AnyRef) match case g: ThreadId @unchecked => g.threadId - } - } end Util diff --git a/project/Dependencies.scala b/project/Dependencies.scala index 8df171001..936f011a9 100644 --- a/project/Dependencies.scala +++ b/project/Dependencies.scala @@ -12,7 +12,7 @@ object Dependencies { // sbt modules private val ioVersion = nightlyVersion.getOrElse("1.10.5") - val zincVersion = nightlyVersion.getOrElse("2.0.0-M8") + val zincVersion = nightlyVersion.getOrElse("2.0.0-M9") private val sbtIO = "org.scala-sbt" %% "io" % ioVersion diff --git a/run/src/main/scala/sbt/Run.scala b/run/src/main/scala/sbt/Run.scala index 8b1511a0c..e867ec0ca 100644 --- a/run/src/main/scala/sbt/Run.scala +++ b/run/src/main/scala/sbt/Run.scala @@ -20,7 +20,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[NioPath], options: Seq[String], log: Logger): Try[Unit] @@ -93,7 +93,7 @@ class Run(private[sbt] val newLoader: Seq[NioPath] => ClassLoader, trapExit: Boo 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 => @@ -141,14 +141,22 @@ class Run(private[sbt] val newLoader: Seq[NioPath] => ClassLoader, trapExit: Boo } 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 { @@ -164,19 +172,40 @@ class Run(private[sbt] val newLoader: Seq[NioPath] => ClassLoader, trapExit: Boo 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. */ @@ -207,4 +236,6 @@ object Run: s"""nonzero exit code returned from $label: $exitCode""".stripMargin ) ) + + private[sbt] lazy val isJava25Plus: Boolean = Properties.isJavaAtLeast("25") end Run diff --git a/sbt-app/src/sbt-test/dependency-management/fallback-dependencies-inter-project/a/src/main/scala/A.scala b/sbt-app/src/sbt-test/dependency-management/fallback-dependencies-inter-project/a/src/main/scala/A.scala deleted file mode 100644 index 954405774..000000000 --- a/sbt-app/src/sbt-test/dependency-management/fallback-dependencies-inter-project/a/src/main/scala/A.scala +++ /dev/null @@ -1,6 +0,0 @@ - -case class A(msg: String) - -object A { - def default = A("OK") -} diff --git a/sbt-app/src/sbt-test/dependency-management/fallback-dependencies-inter-project/b/src/main/scala/Main.scala b/sbt-app/src/sbt-test/dependency-management/fallback-dependencies-inter-project/b/src/main/scala/Main.scala deleted file mode 100644 index 59bb6d372..000000000 --- a/sbt-app/src/sbt-test/dependency-management/fallback-dependencies-inter-project/b/src/main/scala/Main.scala +++ /dev/null @@ -1,9 +0,0 @@ -import java.io.File -import java.nio.file.Files - -object Main extends App { - - val msg = shapeless.Generic[A].to(A.default).head - - Files.write(new File("output").toPath, msg.getBytes("UTF-8")) -} diff --git a/sbt-app/src/sbt-test/dependency-management/fallback-dependencies-inter-project/build.sbt b/sbt-app/src/sbt-test/dependency-management/fallback-dependencies-inter-project/build.sbt deleted file mode 100644 index 98731216b..000000000 --- a/sbt-app/src/sbt-test/dependency-management/fallback-dependencies-inter-project/build.sbt +++ /dev/null @@ -1,13 +0,0 @@ -ThisBuild / scalaVersion := "2.11.12" - -lazy val a = project - .settings( - libraryDependencies += "com.chuusai" %% "shapeless" % "2.3.234" from "https://oss.sonatype.org/content/repositories/releases/com/chuusai/shapeless_2.11/2.3.1/shapeless_2.11-2.3.1.jar" - ) - -lazy val b = project - .dependsOn(a) - -lazy val root = project - .in(file(".")) - .aggregate(a, b) diff --git a/sbt-app/src/sbt-test/dependency-management/fallback-dependencies-inter-project/test b/sbt-app/src/sbt-test/dependency-management/fallback-dependencies-inter-project/test deleted file mode 100644 index ea53e1abb..000000000 --- a/sbt-app/src/sbt-test/dependency-management/fallback-dependencies-inter-project/test +++ /dev/null @@ -1,3 +0,0 @@ -$ delete output -> b/run -$ exists output diff --git a/sbt-app/src/sbt-test/run/jep-512/A.scala b/sbt-app/src/sbt-test/run/jep-512/A.scala new file mode 100644 index 000000000..daceacfa1 --- /dev/null +++ b/sbt-app/src/sbt-test/run/jep-512/A.scala @@ -0,0 +1,7 @@ +package example + +class A { + def main(): Unit = { + println("hi") + } +} diff --git a/sbt-app/src/sbt-test/run/jep-512/build.sbt b/sbt-app/src/sbt-test/run/jep-512/build.sbt new file mode 100644 index 000000000..cfb7fd4c6 --- /dev/null +++ b/sbt-app/src/sbt-test/run/jep-512/build.sbt @@ -0,0 +1,13 @@ +// 2.12.x uses Zinc's compiler bridge +ThisBuild / scalaVersion := "2.12.20" + +@transient +lazy val check = taskKey[Unit]("") + +Compile / run / fork := false + +check := { + if (scala.util.Properties.isJavaAtLeast("25")) + (Compile / fgRun).toTask(" ").value + else () +} diff --git a/sbt-app/src/sbt-test/run/jep-512/pending b/sbt-app/src/sbt-test/run/jep-512/pending new file mode 100644 index 000000000..c28fe4985 --- /dev/null +++ b/sbt-app/src/sbt-test/run/jep-512/pending @@ -0,0 +1,2 @@ +# > run +> check