Skip to content

Commit 8d9542c

Browse files
Merge pull request #13535 from BarkingBad/fix-running-repl
Fix running repl
2 parents e9fb094 + 830987e commit 8d9542c

File tree

2 files changed

+50
-14
lines changed

2 files changed

+50
-14
lines changed

compiler/src/dotty/tools/MainGenericRunner.scala

+44-13
Original file line numberDiff line numberDiff line change
@@ -3,7 +3,7 @@ package dotty.tools
33

44
import scala.annotation.tailrec
55
import scala.io.Source
6-
import scala.util.Try
6+
import scala.util.{ Try, Success, Failure }
77
import java.net.URLClassLoader
88
import sys.process._
99
import java.io.File
@@ -15,12 +15,14 @@ import dotty.tools.dotc.config.Properties.envOrNone
1515
import java.util.jar._
1616
import java.util.jar.Attributes.Name
1717
import dotty.tools.io.Jar
18+
import dotty.tools.runner.ScalaClassLoader
1819

1920
enum ExecuteMode:
2021
case Guess
2122
case Script
2223
case Repl
2324
case Run
25+
case PossibleRun
2426

2527
case class Settings(
2628
verbose: Boolean = false,
@@ -30,14 +32,17 @@ case class Settings(
3032
javaArgs: List[String] = List.empty,
3133
scalaArgs: List[String] = List.empty,
3234
residualArgs: List[String] = List.empty,
35+
possibleEntryPaths: List[String] = List.empty,
3336
scriptArgs: List[String] = List.empty,
3437
targetScript: String = "",
38+
targetToRun: String = "",
3539
save: Boolean = false,
40+
modeShouldBePossibleRun: Boolean = false,
3641
modeShouldBeRun: Boolean = false,
3742
compiler: Boolean = false,
3843
) {
3944
def withExecuteMode(em: ExecuteMode): Settings = this.executeMode match
40-
case ExecuteMode.Guess =>
45+
case ExecuteMode.Guess | ExecuteMode.PossibleRun =>
4146
this.copy(executeMode = em)
4247
case _ =>
4348
println(s"execute_mode==[$executeMode], attempted overwrite by [$em]")
@@ -53,6 +58,9 @@ case class Settings(
5358
def withResidualArgs(args: String*): Settings =
5459
this.copy(residualArgs = residualArgs.appendedAll(args.toList))
5560

61+
def withPossibleEntryPaths(args: String*): Settings =
62+
this.copy(possibleEntryPaths = possibleEntryPaths.appendedAll(args.toList))
63+
5664
def withScriptArgs(args: String*): Settings =
5765
this.copy(scriptArgs = scriptArgs.appendedAll(args.toList))
5866

@@ -64,9 +72,15 @@ case class Settings(
6472
this.copy(exitCode = 2)
6573
end withTargetScript
6674

75+
def withTargetToRun(targetToRun: String): Settings =
76+
this.copy(targetToRun = targetToRun)
77+
6778
def withSave: Settings =
6879
this.copy(save = true)
6980

81+
def withModeShouldBePossibleRun: Settings =
82+
this.copy(modeShouldBePossibleRun = true)
83+
7084
def withModeShouldBeRun: Settings =
7185
this.copy(modeShouldBeRun = true)
7286

@@ -85,8 +99,8 @@ object MainGenericRunner {
8599
def process(args: List[String], settings: Settings): Settings = args match
86100
case Nil =>
87101
settings
88-
case "-run" :: tail =>
89-
process(tail, settings.withExecuteMode(ExecuteMode.Run))
102+
case "-run" :: fqName :: tail =>
103+
process(tail, settings.withExecuteMode(ExecuteMode.Run).withTargetToRun(fqName))
90104
case ("-cp" | "-classpath" | "--class-path") :: cp :: tail =>
91105
process(tail, settings.copy(classPath = settings.classPath.appended(cp)))
92106
case ("-version" | "--version") :: _ =>
@@ -120,7 +134,7 @@ object MainGenericRunner {
120134
.withTargetScript(arg)
121135
.withScriptArgs(tail*)
122136
else
123-
val newSettings = if arg.startsWith("-") then settings else settings.withModeShouldBeRun
137+
val newSettings = if arg.startsWith("-") then settings else settings.withPossibleEntryPaths(arg).withModeShouldBePossibleRun
124138
process(tail, newSettings.withResidualArgs(arg))
125139

126140
def main(args: Array[String]): Unit =
@@ -129,12 +143,27 @@ object MainGenericRunner {
129143
val settings = process(allArgs.toList, Settings())
130144
if settings.exitCode != 0 then System.exit(settings.exitCode)
131145

132-
def run(mode: ExecuteMode): Unit = mode match
146+
def run(settings: Settings): Unit = settings.executeMode match
133147
case ExecuteMode.Repl =>
134148
val properArgs =
135149
List("-classpath", settings.classPath.mkString(classpathSeparator)).filter(Function.const(settings.classPath.nonEmpty))
136150
++ settings.residualArgs
137151
repl.Main.main(properArgs.toArray)
152+
153+
case ExecuteMode.PossibleRun =>
154+
val newClasspath = (settings.classPath :+ ".").map(File(_).toURI.toURL)
155+
import dotty.tools.runner.RichClassLoader._
156+
val newClassLoader = ScalaClassLoader.fromURLsParallelCapable(newClasspath)
157+
val targetToRun = settings.possibleEntryPaths.to(LazyList).find { entryPath =>
158+
newClassLoader.tryToLoadClass(entryPath).orElse {
159+
Option.when(Jar.isJarOrZip(dotty.tools.io.Path(entryPath)))(Jar(entryPath).mainClass).flatten
160+
}.isDefined
161+
}
162+
targetToRun match
163+
case Some(fqName) =>
164+
run(settings.withTargetToRun(fqName).withResidualArgs(settings.residualArgs.filter { _ != fqName }*).withExecuteMode(ExecuteMode.Run))
165+
case None =>
166+
run(settings.withExecuteMode(ExecuteMode.Repl))
138167
case ExecuteMode.Run =>
139168
val scalaClasspath = ClasspathFromClassloader(Thread.currentThread().getContextClassLoader).split(classpathSeparator)
140169

@@ -146,9 +175,9 @@ object MainGenericRunner {
146175
cp
147176
val newClasspath = (settings.classPath ++ removeCompiler(scalaClasspath) :+ ".").map(File(_).toURI.toURL)
148177

149-
val res = ObjectRunner.runAndCatch(newClasspath, settings.residualArgs.head, settings.residualArgs.drop(1)).flatMap {
150-
case ex: ClassNotFoundException if ex.getMessage == settings.residualArgs.head =>
151-
val file = settings.residualArgs.head
178+
val res = ObjectRunner.runAndCatch(newClasspath, settings.targetToRun, settings.residualArgs).flatMap {
179+
case ex: ClassNotFoundException if ex.getMessage == settings.targetToRun =>
180+
val file = settings.targetToRun
152181
Jar(file).mainClass match
153182
case Some(mc) =>
154183
ObjectRunner.runAndCatch(newClasspath :+ File(file).toURI.toURL, mc, settings.residualArgs)
@@ -167,12 +196,14 @@ object MainGenericRunner {
167196
++ settings.scriptArgs
168197
scripting.Main.main(properArgs.toArray)
169198
case ExecuteMode.Guess =>
170-
if settings.modeShouldBeRun then
171-
run(ExecuteMode.Run)
199+
if settings.modeShouldBePossibleRun then
200+
run(settings.withExecuteMode(ExecuteMode.PossibleRun))
201+
else if settings.modeShouldBeRun then
202+
run(settings.withExecuteMode(ExecuteMode.Run))
172203
else
173-
run(ExecuteMode.Repl)
204+
run(settings.withExecuteMode(ExecuteMode.Repl))
174205

175-
run(settings.executeMode)
206+
run(settings)
176207

177208

178209
def errorFn(str: String, e: Option[Throwable] = None, isFailure: Boolean = true): Boolean = {

compiler/test-coursier/dotty/tools/coursier/CoursierScalaTests.scala

+6-1
Original file line numberDiff line numberDiff line change
@@ -71,7 +71,7 @@ class CoursierScalaTests:
7171
emptyArgsEqualsRepl()
7272

7373
def run() =
74-
val output = CoursierScalaTests.csScalaCmd("-run", "-classpath", scripts("/run").head.getParentFile.getParent, "run.myfile")
74+
val output = CoursierScalaTests.csScalaCmd("-classpath", scripts("/run").head.getParentFile.getParent, "-run", "run.myfile")
7575
assertEquals(output.mkString("\n"), "Hello")
7676
run()
7777

@@ -117,6 +117,11 @@ class CoursierScalaTests:
117117
assertEquals(output4.mkString("\n"), "Hello")
118118
compileFilesToJarAndRun()
119119

120+
def replWithArgs() =
121+
val output = CoursierScalaTests.csScalaCmd("-source", "3.0-migration")
122+
assertTrue(output.mkString("\n").contains("Unable to create a system terminal")) // Scala attempted to create REPL so we can assume it is working
123+
replWithArgs()
124+
120125
object CoursierScalaTests:
121126

122127
def execCmd(command: String, options: String*): List[String] =

0 commit comments

Comments
 (0)