Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

Fix running repl #13535

Merged
merged 2 commits into from
Sep 16, 2021
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
57 changes: 44 additions & 13 deletions compiler/src/dotty/tools/MainGenericRunner.scala
Original file line number Diff line number Diff line change
Expand Up @@ -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
Expand All @@ -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,
Expand All @@ -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]")
Expand All @@ -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))

Expand All @@ -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)

Expand All @@ -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") :: _ =>
Expand Down Expand Up @@ -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 =
Expand All @@ -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)

Expand All @@ -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)
Expand All @@ -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 = {
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -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()

Expand Down Expand Up @@ -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] =
Expand Down