Skip to content

Commit 837a204

Browse files
Scripting Solution: scala can compile & run *.scala files
1 parent 8706aa5 commit 837a204

File tree

2 files changed

+101
-0
lines changed

2 files changed

+101
-0
lines changed
Lines changed: 22 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,22 @@
1+
package dotty.tools.scripting
2+
3+
import java.io.File
4+
5+
/** Main entry point to the Scripting execution engine */
6+
object Main:
7+
/** All arguments before -script <target_script> are compiler arguments.
8+
All arguments afterwards are script arguments.*/
9+
def distinguishArgs(args: Array[String]): (Array[String], File, Array[String]) =
10+
val (compilerArgs, rest) = args.splitAt(args.indexOf("-script"))
11+
val file = File(rest(1))
12+
val scriptArgs = rest.drop(2)
13+
(compilerArgs, file, scriptArgs)
14+
end distinguishArgs
15+
16+
def main(args: Array[String]): Unit =
17+
val (compilerArgs, scriptFile, scriptArgs) = distinguishArgs(args)
18+
try ScriptingDriver(compilerArgs, scriptFile, scriptArgs).compileAndRun()
19+
catch
20+
case ScriptingException(msg) =>
21+
println(s"Error: $msg")
22+
sys.exit(1)
Lines changed: 79 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,79 @@
1+
package dotty.tools.scripting
2+
3+
import java.nio.file.{ Files, Path }
4+
import java.io.File
5+
import java.net.{ URL, URLClassLoader }
6+
import java.lang.reflect.{ Modifier, Method }
7+
8+
import scala.jdk.CollectionConverters._
9+
10+
import dotty.tools.dotc.{ Driver, Compiler }
11+
import dotty.tools.dotc.core.Contexts, Contexts.{ Context, ContextBase, ctx }
12+
import dotty.tools.dotc.config.CompilerCommand
13+
import dotty.tools.io.{ PlainDirectory, Directory }
14+
import dotty.tools.dotc.reporting.Reporter
15+
import dotty.tools.dotc.config.Settings.Setting._
16+
17+
import sys.process._
18+
19+
class ScriptingDriver(compilerArgs: Array[String], scriptFile: File, scriptArgs: Array[String]) extends Driver:
20+
def compileAndRun(): Unit =
21+
val outDir = Files.createTempDirectory("scala3-scripting")
22+
val (toCompile, rootCtx) = setup(compilerArgs :+ scriptFile.getAbsolutePath, initCtx.fresh)
23+
given Context = rootCtx.fresh.setSetting(rootCtx.settings.outputDir,
24+
new PlainDirectory(Directory(outDir)))
25+
26+
if doCompile(newCompiler, toCompile).hasErrors then
27+
throw ScriptingException("Errors encountered during compilation")
28+
29+
try detectMainMethod(outDir, ctx.settings.classpath.value).invoke(null, scriptArgs)
30+
catch
31+
case e: java.lang.reflect.InvocationTargetException =>
32+
throw e.getCause
33+
34+
end compileAndRun
35+
36+
private def detectMainMethod(outDir: Path, classpath: String): Method =
37+
val outDirURL = outDir.toUri.toURL
38+
val classpathUrls = classpath.split(":").map(File(_).toURI.toURL)
39+
val cl = URLClassLoader(classpathUrls :+ outDirURL)
40+
41+
def collectMainMethods(target: File, path: String): List[Method] =
42+
val nameWithoutExtension = target.getName.takeWhile(_ != '.')
43+
val targetPath =
44+
if path.nonEmpty then s"${path}.${nameWithoutExtension}"
45+
else nameWithoutExtension
46+
47+
if target.isDirectory then
48+
for
49+
packageMember <- target.listFiles.toList
50+
membersMainMethod <- collectMainMethods(packageMember, targetPath)
51+
yield membersMainMethod
52+
else if target.getName.endsWith(".class") then
53+
val cls = cl.loadClass(targetPath)
54+
val method =
55+
try cls.getMethod("main", classOf[Array[String]])
56+
catch
57+
case _: java.lang.NoSuchMethodException => null
58+
59+
if method != null && Modifier.isStatic(method.getModifiers) then List(method)
60+
else Nil
61+
else Nil
62+
end collectMainMethods
63+
64+
val candidates = for
65+
file <- outDir.toFile.listFiles.toList
66+
method <- collectMainMethods(file, "")
67+
yield method
68+
69+
candidates match
70+
case Nil =>
71+
throw ScriptingException("No main methods detected in your script")
72+
case _ :: _ :: _ =>
73+
throw ScriptingException("More than one main method detected in your script")
74+
case m :: Nil => m
75+
end match
76+
end detectMainMethod
77+
end ScriptingDriver
78+
79+
case class ScriptingException(msg: String) extends RuntimeException(msg)

0 commit comments

Comments
 (0)