From dc17145a07427f41a79db343a2e5c0885b09e56b Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Tue, 14 Sep 2021 18:45:13 +0200 Subject: [PATCH 1/2] Fix running REPL from MainGenericRunner with arguments --- .../src/dotty/tools/MainGenericRunner.scala | 61 +++++++++++++------ 1 file changed, 44 insertions(+), 17 deletions(-) diff --git a/compiler/src/dotty/tools/MainGenericRunner.scala b/compiler/src/dotty/tools/MainGenericRunner.scala index 5221e8b642f5..801210a66104 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 @@ -21,6 +21,7 @@ enum ExecuteMode: case Script case Repl case Run + case PossibleRun case class Settings( verbose: Boolean = false, @@ -30,19 +31,22 @@ 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 = "", + targetFqName: String = "", save: Boolean = false, + modeShouldBePossibleRun: Boolean = false, modeShouldBeRun: Boolean = false, compiler: Boolean = false, ) { - def withExecuteMode(em: ExecuteMode): Settings = this.executeMode match - case ExecuteMode.Guess => - this.copy(executeMode = em) - case _ => - println(s"execute_mode==[$executeMode], attempted overwrite by [$em]") - this.copy(exitCode = 1) - end withExecuteMode + def withExecuteMode(em: ExecuteMode): Settings = //this.executeMode match + // case ExecuteMode.Guess => + this.copy(executeMode = em) + // case _ => + // println(s"execute_mode==[$executeMode], attempted overwrite by [$em]") + // this.copy(exitCode = 1) + // end withExecuteMode def withScalaArgs(args: String*): Settings = this.copy(scalaArgs = scalaArgs.appendedAll(args.toList)) @@ -53,6 +57,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 +71,15 @@ case class Settings( this.copy(exitCode = 2) end withTargetScript + def withTargetFqName(targetFqName: String): Settings = + this.copy(targetFqName = targetFqName) + def withSave: Settings = this.copy(save = true) + def withModeShouldBePossibleRun: Settings = + this.copy(modeShouldBePossibleRun = true) + def withModeShouldBeRun: Settings = this.copy(modeShouldBeRun = true) @@ -85,8 +98,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).withTargetFqName(fqName)) case ("-cp" | "-classpath" | "--class-path") :: cp :: tail => process(tail, settings.copy(classPath = settings.classPath.appended(cp))) case ("-version" | "--version") :: _ => @@ -120,7 +133,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 +142,24 @@ 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 targetFqName = settings.possibleEntryPaths.find { entryPath => + Try(Thread.currentThread().getContextClassLoader.loadClass(entryPath)) match + case Failure(_) => false + case Success(_) => true + } + targetFqName match + case Some(fqName) => + run(settings.withTargetFqName(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,7 +171,7 @@ 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 { + val res = ObjectRunner.runAndCatch(newClasspath, settings.targetFqName, settings.residualArgs).flatMap { case ex: ClassNotFoundException if ex.getMessage == settings.residualArgs.head => val file = settings.residualArgs.head Jar(file).mainClass match @@ -167,12 +192,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 = { From 830987e4e4dd4662553a47c25d1c9a4402609707 Mon Sep 17 00:00:00 2001 From: Andrzej Ratajczak Date: Wed, 15 Sep 2021 12:44:03 +0200 Subject: [PATCH 2/2] Add unit tests --- .../src/dotty/tools/MainGenericRunner.scala | 44 ++++++++++--------- .../tools/coursier/CoursierScalaTests.scala | 7 ++- 2 files changed, 30 insertions(+), 21 deletions(-) diff --git a/compiler/src/dotty/tools/MainGenericRunner.scala b/compiler/src/dotty/tools/MainGenericRunner.scala index 801210a66104..af23cf08728d 100644 --- a/compiler/src/dotty/tools/MainGenericRunner.scala +++ b/compiler/src/dotty/tools/MainGenericRunner.scala @@ -15,6 +15,7 @@ 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 @@ -34,19 +35,19 @@ case class Settings( possibleEntryPaths: List[String] = List.empty, scriptArgs: List[String] = List.empty, targetScript: String = "", - targetFqName: 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 => - this.copy(executeMode = em) - // case _ => - // println(s"execute_mode==[$executeMode], attempted overwrite by [$em]") - // this.copy(exitCode = 1) - // end withExecuteMode + def withExecuteMode(em: ExecuteMode): Settings = this.executeMode match + case ExecuteMode.Guess | ExecuteMode.PossibleRun => + this.copy(executeMode = em) + case _ => + println(s"execute_mode==[$executeMode], attempted overwrite by [$em]") + this.copy(exitCode = 1) + end withExecuteMode def withScalaArgs(args: String*): Settings = this.copy(scalaArgs = scalaArgs.appendedAll(args.toList)) @@ -71,8 +72,8 @@ case class Settings( this.copy(exitCode = 2) end withTargetScript - def withTargetFqName(targetFqName: String): Settings = - this.copy(targetFqName = targetFqName) + def withTargetToRun(targetToRun: String): Settings = + this.copy(targetToRun = targetToRun) def withSave: Settings = this.copy(save = true) @@ -99,7 +100,7 @@ object MainGenericRunner { case Nil => settings case "-run" :: fqName :: tail => - process(tail, settings.withExecuteMode(ExecuteMode.Run).withTargetFqName(fqName)) + 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") :: _ => @@ -150,14 +151,17 @@ object MainGenericRunner { repl.Main.main(properArgs.toArray) case ExecuteMode.PossibleRun => - val targetFqName = settings.possibleEntryPaths.find { entryPath => - Try(Thread.currentThread().getContextClassLoader.loadClass(entryPath)) match - case Failure(_) => false - case Success(_) => true + 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 } - targetFqName match + targetToRun match case Some(fqName) => - run(settings.withTargetFqName(fqName).withResidualArgs(settings.residualArgs.filter { _ != fqName }*).withExecuteMode(ExecuteMode.Run)) + run(settings.withTargetToRun(fqName).withResidualArgs(settings.residualArgs.filter { _ != fqName }*).withExecuteMode(ExecuteMode.Run)) case None => run(settings.withExecuteMode(ExecuteMode.Repl)) case ExecuteMode.Run => @@ -171,9 +175,9 @@ object MainGenericRunner { cp val newClasspath = (settings.classPath ++ removeCompiler(scalaClasspath) :+ ".").map(File(_).toURI.toURL) - val res = ObjectRunner.runAndCatch(newClasspath, settings.targetFqName, settings.residualArgs).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) 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] =