Skip to content

Commit d0c0eff

Browse files
authored
Merge pull request #13081 from BarkingBad/scala-sh-rewrite
Scala sh rewrite
2 parents 2279afc + 314c3c9 commit d0c0eff

File tree

11 files changed

+602
-202
lines changed

11 files changed

+602
-202
lines changed

.github/workflows/ci.yaml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -111,7 +111,7 @@ jobs:
111111

112112
- name: Cmd Tests
113113
run: |
114-
./project/scripts/sbt ";scala3-bootstrapped/compile ;scala3-bootstrapped/test;sjsSandbox/run;sjsSandbox/test;sjsJUnitTests/test;sjsCompilerTests/test ;sbt-test/scripted scala2-compat/* ;configureIDE ;stdlib-bootstrapped/test:run ;stdlib-bootstrapped-tasty-tests/test"
114+
./project/scripts/sbt ";scala3-bootstrapped/compile; scala3-bootstrapped/test;sjsSandbox/run;sjsSandbox/test;sjsJUnitTests/test;sjsCompilerTests/test ;sbt-test/scripted scala2-compat/* ;configureIDE ;stdlib-bootstrapped/test:run ;stdlib-bootstrapped-tasty-tests/test; scala3-compiler-bootstrapped/scala3CompilerCoursierTest:test"
115115
./project/scripts/bootstrapCmdTests
116116
117117
- name: MiMa

.gitignore

Lines changed: 6 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -96,3 +96,9 @@ community-build/dotty-community-build-deps
9696

9797
# Bloop
9898
.bsp
99+
100+
# Coursier
101+
cs
102+
103+
# Coursier test product
104+
compiler/test-coursier/run/myfile.jar
Lines changed: 156 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,156 @@
1+
package dotty.tools
2+
3+
4+
import scala.annotation.tailrec
5+
import scala.io.Source
6+
import scala.util.Try
7+
import java.net.URLClassLoader
8+
import sys.process._
9+
import java.io.File
10+
import java.lang.Thread
11+
import scala.annotation.internal.sharable
12+
import dotty.tools.dotc.util.ClasspathFromClassloader
13+
import dotty.tools.runner.ObjectRunner
14+
import dotty.tools.dotc.config.Properties.envOrNone
15+
16+
enum ExecuteMode:
17+
case Guess
18+
case Script
19+
case Repl
20+
case Run
21+
22+
case class Settings(
23+
verbose: Boolean = false,
24+
classPath: List[String] = List.empty,
25+
executeMode: ExecuteMode = ExecuteMode.Guess,
26+
exitCode: Int = 0,
27+
javaArgs: List[String] = List.empty,
28+
scalaArgs: List[String] = List.empty,
29+
residualArgs: List[String] = List.empty,
30+
scriptArgs: List[String] = List.empty,
31+
targetScript: String = "",
32+
save: Boolean = false,
33+
modeShouldBeRun: Boolean = false,
34+
) {
35+
def withExecuteMode(em: ExecuteMode): Settings = this.executeMode match
36+
case ExecuteMode.Guess =>
37+
this.copy(executeMode = em)
38+
case _ =>
39+
println(s"execute_mode==[$executeMode], attempted overwrite by [$em]")
40+
this.copy(exitCode = 1)
41+
end withExecuteMode
42+
43+
def withScalaArgs(args: String*): Settings =
44+
this.copy(scalaArgs = scalaArgs.appendedAll(args.toList))
45+
46+
def withJavaArgs(args: String*): Settings =
47+
this.copy(javaArgs = javaArgs.appendedAll(args.toList))
48+
49+
def withResidualArgs(args: String*): Settings =
50+
this.copy(residualArgs = residualArgs.appendedAll(args.toList))
51+
52+
def withScriptArgs(args: String*): Settings =
53+
this.copy(scriptArgs = scriptArgs.appendedAll(args.toList))
54+
55+
def withTargetScript(file: String): Settings =
56+
Try(Source.fromFile(file)).toOption match
57+
case Some(_) => this.copy(targetScript = file)
58+
case None =>
59+
println(s"not found $file")
60+
this.copy(exitCode = 2)
61+
end withTargetScript
62+
63+
def withSave: Settings =
64+
this.copy(save = true)
65+
66+
def withModeShouldBeRun: Settings =
67+
this.copy(modeShouldBeRun = true)
68+
}
69+
70+
object MainGenericRunner {
71+
72+
val classpathSeparator = File.pathSeparator
73+
74+
@sharable val javaOption = raw"""-J(.*)""".r
75+
@sharable val scalaOption = raw"""@.*""".r
76+
@sharable val colorOption = raw"""-color:.*""".r
77+
@tailrec
78+
def process(args: List[String], settings: Settings): Settings = args match
79+
case Nil =>
80+
settings
81+
case "-run" :: tail =>
82+
process(tail, settings.withExecuteMode(ExecuteMode.Run))
83+
case ("-cp" | "-classpath" | "--class-path") :: cp :: tail =>
84+
process(tail, settings.copy(classPath = settings.classPath.appended(cp)))
85+
case ("-version" | "--version") :: _ =>
86+
settings.copy(
87+
executeMode = ExecuteMode.Repl,
88+
residualArgs = List("-version")
89+
)
90+
case ("-v" | "-verbose" | "--verbose") :: tail =>
91+
process(
92+
tail,
93+
settings.copy(
94+
verbose = true,
95+
residualArgs = settings.residualArgs :+ "-verbose"
96+
)
97+
)
98+
case "-save" :: tail =>
99+
process(tail, settings.withSave)
100+
case (o @ javaOption(striped)) :: tail =>
101+
process(tail, settings.withJavaArgs(striped).withScalaArgs(o))
102+
case (o @ scalaOption(_*)) :: tail =>
103+
process(tail, settings.withScalaArgs(o))
104+
case (o @ colorOption(_*)) :: tail =>
105+
process(tail, settings.withScalaArgs(o))
106+
case arg :: tail =>
107+
val line = Try(Source.fromFile(arg).getLines.toList).toOption.flatMap(_.headOption)
108+
if arg.endsWith(".scala") || arg.endsWith(".sc") || (line.nonEmpty && raw"#!.*scala".r.matches(line.get)) then
109+
settings
110+
.withExecuteMode(ExecuteMode.Script)
111+
.withTargetScript(arg)
112+
.withScriptArgs(tail*)
113+
else
114+
val newSettings = if arg.startsWith("-") then settings else settings.withModeShouldBeRun
115+
process(tail, newSettings.withResidualArgs(arg))
116+
117+
def main(args: Array[String]): Unit =
118+
val scalaOpts = envOrNone("SCALA_OPTS").toArray.flatMap(_.split(" "))
119+
val allArgs = scalaOpts ++ args
120+
val settings = process(allArgs.toList, Settings())
121+
if settings.exitCode != 0 then System.exit(settings.exitCode)
122+
123+
def run(mode: ExecuteMode): Unit = mode match
124+
case ExecuteMode.Repl =>
125+
val properArgs =
126+
List("-classpath", settings.classPath.mkString(classpathSeparator)).filter(Function.const(settings.classPath.nonEmpty))
127+
++ settings.residualArgs
128+
repl.Main.main(properArgs.toArray)
129+
case ExecuteMode.Run =>
130+
val scalaClasspath = ClasspathFromClassloader(Thread.currentThread().getContextClassLoader).split(classpathSeparator)
131+
val newClasspath = (settings.classPath ++ scalaClasspath :+ ".").map(File(_).toURI.toURL)
132+
errorFn("", ObjectRunner.runAndCatch(newClasspath, settings.residualArgs.head, settings.residualArgs.drop(1)))
133+
case ExecuteMode.Script =>
134+
val properArgs =
135+
List("-classpath", settings.classPath.mkString(classpathSeparator)).filter(Function.const(settings.classPath.nonEmpty))
136+
++ settings.residualArgs
137+
++ (if settings.save then List("-save") else Nil)
138+
++ List("-script", settings.targetScript)
139+
++ settings.scalaArgs
140+
++ settings.scriptArgs
141+
scripting.Main.main(properArgs.toArray)
142+
case ExecuteMode.Guess =>
143+
if settings.modeShouldBeRun then
144+
run(ExecuteMode.Run)
145+
else
146+
run(ExecuteMode.Repl)
147+
148+
run(settings.executeMode)
149+
150+
151+
def errorFn(str: String, e: Option[Throwable] = None, isFailure: Boolean = true): Boolean = {
152+
if (str.nonEmpty) Console.err.println(str)
153+
e.foreach(_.printStackTrace())
154+
!isFailure
155+
}
156+
}
Lines changed: 48 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,48 @@
1+
package dotty.tools
2+
package runner
3+
4+
import java.net.URL
5+
import scala.util.control.NonFatal
6+
import java.lang.reflect.InvocationTargetException
7+
import java.lang.reflect.UndeclaredThrowableException
8+
import java.util.concurrent.ExecutionException
9+
10+
/**
11+
* This is a copy implementation from scala/scala scala.tools.nsc.CommonRunner trait
12+
*/
13+
trait CommonRunner {
14+
/** Run a given object, specified by name, using a
15+
* specified classpath and argument list.
16+
*
17+
* @throws java.lang.ClassNotFoundException
18+
* @throws java.lang.NoSuchMethodException
19+
* @throws java.lang.reflect.InvocationTargetException
20+
*/
21+
def run(urls: Seq[URL], objectName: String, arguments: Seq[String]): Unit = {
22+
import RichClassLoader._
23+
ScalaClassLoader.fromURLsParallelCapable(urls).run(objectName, arguments)
24+
}
25+
26+
/** Catches any non-fatal exception thrown by run (in the case of InvocationTargetException,
27+
* unwrapping it) and returns it in an Option.
28+
*/
29+
def runAndCatch(urls: Seq[URL], objectName: String, arguments: Seq[String]): Option[Throwable] =
30+
try { run(urls, objectName, arguments) ; None }
31+
catch { case NonFatal(e) => Some(rootCause(e)) }
32+
33+
private def rootCause(x: Throwable): Throwable = x match {
34+
case _: InvocationTargetException |
35+
_: ExceptionInInitializerError |
36+
_: UndeclaredThrowableException |
37+
_: ExecutionException
38+
if x.getCause != null =>
39+
rootCause(x.getCause)
40+
case _ => x
41+
}
42+
}
43+
44+
/** An object that runs another object specified by name.
45+
*
46+
* @author Lex Spoon
47+
*/
48+
object ObjectRunner extends CommonRunner

0 commit comments

Comments
 (0)