タスクランナーを作る/動的コンパイルさせるコード
from タスクランナーを作る
動的コンパイルさせるコード
code:runner.scala
//> using scala 3.8.3
//> using dep org.scala-lang::scala3-compiler:3.8.3
import dotty.tools.dotc.Main
import java.net.URLClassLoader
import java.nio.file.{Files, Path, Paths}
object ScriptRunner {
private val EntryPoint = "__scriptMain"
def run(
sourceFile: Path,
extraImports: SeqString,
programArgs: ArrayString = Array.empty
): Unit = {
val srcDir = Files.createTempDirectory("scala-src-")
val outDir = Files.createTempDirectory("scala-out-")
try {
val body = Files.readString(sourceFile)
val importLines = extraImports.map(i => s"import $i").mkString("\n")
// ファイル本体を @main 関数で包む
val wrapped =
s"""$importLines
|
|@main def $EntryPoint(args: String*): Unit = {
|$body
|}
|""".stripMargin
val patched = srcDir.resolve(sourceFile.getFileName.toString)
Files.writeString(patched, wrapped)
val classpath = System.getProperty("java.class.path")
val args = Array(
"-d", outDir.toString,
"-classpath", classpath,
patched.toString
)
val reporter = Main.process(args)
if (reporter.hasErrors) {
throw new RuntimeException(
s"コンパイルに失敗しました(エラー件数: ${reporter.errorCount})"
)
}
val loader = new URLClassLoader(
Array(outDir.toUri.toURL),
getClass.getClassLoader
)
val cls = loader.loadClass(EntryPoint)
val mainM = cls.getMethod("main", classOf[ArrayString])
mainM.invoke(null, programArgs)
} finally {
def deleteRecursively(p: Path): Unit = {
if (Files.isDirectory(p)) {
Files.list(p).forEach(deleteRecursively)
}
Files.deleteIfExists(p)
}
deleteRecursively(srcDir)
deleteRecursively(outDir)
}
}
}
@main def runScript(programArgs: String*): Unit = {
ScriptRunner.run(
sourceFile = Paths.get("Hello.scala"),
extraImports = Seq(
"scala.collection.mutable",
"java.time.LocalDate"
),
programArgs = programArgs.toArray
)
}