diff --git a/compiler/src/dotty/tools/MainGenericRunner.scala b/compiler/src/dotty/tools/MainGenericRunner.scala index 5221e8b642f5..af23cf08728d 100644 --- a/compiler/src/dotty/tools/MainGenericRunner.scala +++ b/compiler/src/dotty/tools/MainGenericRunner.scala @@ -3,7 +3,7 @@ package dotty.tools import scala.annotation.tailrec import scala.io.Source -import scala.util.Try +import scala.util.{ Try, Success, Failure } import java.net.URLClassLoader import sys.process._ import java.io.File @@ -15,12 +15,14 @@ import dotty.tools.dotc.config.Properties.envOrNone import java.util.jar._ import java.util.jar.Attributes.Name import dotty.tools.io.Jar +import dotty.tools.runner.ScalaClassLoader enum ExecuteMode: case Guess case Script case Repl case Run + case PossibleRun case class Settings( verbose: Boolean = false, @@ -30,14 +32,17 @@ case class Settings( javaArgs: List[String] = List.empty, scalaArgs: List[String] = List.empty, residualArgs: List[String] = List.empty, + possibleEntryPaths: List[String] = List.empty, scriptArgs: List[String] = List.empty, targetScript: String = "", + targetToRun: String = "", save: Boolean = false, + modeShouldBePossibleRun: Boolean = false, modeShouldBeRun: Boolean = false, compiler: Boolean = false, ) { def withExecuteMode(em: ExecuteMode): Settings = this.executeMode match - case ExecuteMode.Guess => + case ExecuteMode.Guess | ExecuteMode.PossibleRun => this.copy(executeMode = em) case _ => println(s"execute_mode==[$executeMode], attempted overwrite by [$em]") @@ -53,6 +58,9 @@ case class Settings( def withResidualArgs(args: String*): Settings = this.copy(residualArgs = residualArgs.appendedAll(args.toList)) + def withPossibleEntryPaths(args: String*): Settings = + this.copy(possibleEntryPaths = possibleEntryPaths.appendedAll(args.toList)) + def withScriptArgs(args: String*): Settings = this.copy(scriptArgs = scriptArgs.appendedAll(args.toList)) @@ -64,9 +72,15 @@ case class Settings( this.copy(exitCode = 2) end withTargetScript + def withTargetToRun(targetToRun: String): Settings = + this.copy(targetToRun = targetToRun) + def withSave: Settings = this.copy(save = true) + def withModeShouldBePossibleRun: Settings = + this.copy(modeShouldBePossibleRun = true) + def withModeShouldBeRun: Settings = this.copy(modeShouldBeRun = true) @@ -85,8 +99,8 @@ object MainGenericRunner { def process(args: List[String], settings: Settings): Settings = args match case Nil => settings - case "-run" :: tail => - process(tail, settings.withExecuteMode(ExecuteMode.Run)) + case "-run" :: fqName :: tail => + process(tail, settings.withExecuteMode(ExecuteMode.Run).withTargetToRun(fqName)) case ("-cp" | "-classpath" | "--class-path") :: cp :: tail => process(tail, settings.copy(classPath = settings.classPath.appended(cp))) case ("-version" | "--version") :: _ => @@ -120,7 +134,7 @@ object MainGenericRunner { .withTargetScript(arg) .withScriptArgs(tail*) else - val newSettings = if arg.startsWith("-") then settings else settings.withModeShouldBeRun + val newSettings = if arg.startsWith("-") then settings else settings.withPossibleEntryPaths(arg).withModeShouldBePossibleRun process(tail, newSettings.withResidualArgs(arg)) def main(args: Array[String]): Unit = @@ -129,12 +143,27 @@ object MainGenericRunner { val settings = process(allArgs.toList, Settings()) if settings.exitCode != 0 then System.exit(settings.exitCode) - def run(mode: ExecuteMode): Unit = mode match + def run(settings: Settings): Unit = settings.executeMode match case ExecuteMode.Repl => val properArgs = List("-classpath", settings.classPath.mkString(classpathSeparator)).filter(Function.const(settings.classPath.nonEmpty)) ++ settings.residualArgs repl.Main.main(properArgs.toArray) + + case ExecuteMode.PossibleRun => + val newClasspath = (settings.classPath :+ ".").map(File(_).toURI.toURL) + import dotty.tools.runner.RichClassLoader._ + val newClassLoader = ScalaClassLoader.fromURLsParallelCapable(newClasspath) + val targetToRun = settings.possibleEntryPaths.to(LazyList).find { entryPath => + newClassLoader.tryToLoadClass(entryPath).orElse { + Option.when(Jar.isJarOrZip(dotty.tools.io.Path(entryPath)))(Jar(entryPath).mainClass).flatten + }.isDefined + } + targetToRun match + case Some(fqName) => + run(settings.withTargetToRun(fqName).withResidualArgs(settings.residualArgs.filter { _ != fqName }*).withExecuteMode(ExecuteMode.Run)) + case None => + run(settings.withExecuteMode(ExecuteMode.Repl)) case ExecuteMode.Run => val scalaClasspath = ClasspathFromClassloader(Thread.currentThread().getContextClassLoader).split(classpathSeparator) @@ -146,9 +175,9 @@ object MainGenericRunner { cp val newClasspath = (settings.classPath ++ removeCompiler(scalaClasspath) :+ ".").map(File(_).toURI.toURL) - val res = ObjectRunner.runAndCatch(newClasspath, settings.residualArgs.head, settings.residualArgs.drop(1)).flatMap { - case ex: ClassNotFoundException if ex.getMessage == settings.residualArgs.head => - val file = settings.residualArgs.head + val res = ObjectRunner.runAndCatch(newClasspath, settings.targetToRun, settings.residualArgs).flatMap { + case ex: ClassNotFoundException if ex.getMessage == settings.targetToRun => + val file = settings.targetToRun Jar(file).mainClass match case Some(mc) => ObjectRunner.runAndCatch(newClasspath :+ File(file).toURI.toURL, mc, settings.residualArgs) @@ -167,12 +196,14 @@ object MainGenericRunner { ++ settings.scriptArgs scripting.Main.main(properArgs.toArray) case ExecuteMode.Guess => - if settings.modeShouldBeRun then - run(ExecuteMode.Run) + if settings.modeShouldBePossibleRun then + run(settings.withExecuteMode(ExecuteMode.PossibleRun)) + else if settings.modeShouldBeRun then + run(settings.withExecuteMode(ExecuteMode.Run)) else - run(ExecuteMode.Repl) + run(settings.withExecuteMode(ExecuteMode.Repl)) - run(settings.executeMode) + run(settings) def errorFn(str: String, e: Option[Throwable] = None, isFailure: Boolean = true): Boolean = { diff --git a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala index a9c9809b1be7..e932ac54089a 100644 --- a/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala +++ b/compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala @@ -71,7 +71,7 @@ class CoursierScalaTests: emptyArgsEqualsRepl() def run() = - val output = CoursierScalaTests.csScalaCmd("-run", "-classpath", scripts("/run").head.getParentFile.getParent, "run.myfile") + val output = CoursierScalaTests.csScalaCmd("-classpath", scripts("/run").head.getParentFile.getParent, "-run", "run.myfile") assertEquals(output.mkString("\n"), "Hello") run() @@ -117,6 +117,11 @@ class CoursierScalaTests: assertEquals(output4.mkString("\n"), "Hello") compileFilesToJarAndRun() + def replWithArgs() = + val output = CoursierScalaTests.csScalaCmd("-source", "3.0-migration") + assertTrue(output.mkString("\n").contains("Unable to create a system terminal")) // Scala attempted to create REPL so we can assume it is working + replWithArgs() + object CoursierScalaTests: def execCmd(command: String, options: String*): List[String] =