/* NSC -- new Scala compiler * Copyright 2005-2007 LAMP/EPFL * @author Martin Odersky */ // $Id$ package scala.tools.nsc import java.io.{BufferedReader, File, FileInputStream, FileOutputStream, FileReader, InputStreamReader, PrintWriter, FileWriter} import java.lang.reflect.InvocationTargetException import java.net.URL import java.util.jar.{JarEntry, JarOutputStream} import scala.tools.nsc.io.PlainFile import scala.tools.nsc.reporters.{Reporter,ConsoleReporter} import scala.tools.nsc.util.{ClassPath, CompoundSourceFile, BatchSourceFile, SourceFile, SourceFileFragment} /** An object that runs Scala code in script files. * *
For example, here is a complete Scala script on Unix: *
* #!/bin/sh * exec scala "$0" "$@" * !# * Console.println("Hello, world!") * argv.toList foreach Console.println **
And here is a batch file example on Windows XP:
** ::#! * @echo off * call scala %0 %* * goto :eof * ::!# * Console.println("Hello, world!") * argv.toList foreach Console.println ** * @author Lex Spoon * @version 1.0, 15/05/2006 * @todo It would be better if error output went to stderr instead * of stdout... */ object ScriptRunner { /** Default name to use for the wrapped script */ val defaultScriptMain = "Main" /** Exception used internally */ case class ScriptException(msg: String) extends Exception /** Pick a main object name from the specified settings */ def scriptMain(settings: Settings) = if (settings.script.value == "") defaultScriptMain else settings.script.value /** Choose a jar filename to hold the compiled version * of a script */ private def jarFileFor(scriptFile: String): File = { val filename = if (scriptFile.matches(".*\\.[^.\\\\/]*")) scriptFile.replaceFirst("\\.[^.\\\\/]*$", ".jar") else scriptFile + ".jar" new File(filename) } /** Try to create a jar file out of all the contents * of the directory
sourcePath
.
*/
private def tryMakeJar(jarFile: File, sourcePath: File) = {
try {
val jarFileStream = new FileOutputStream(jarFile)
val jar = new JarOutputStream(jarFileStream)
val buf = new Array[Byte](10240)
def addFromDir(dir: File, prefix: String) {
for (entry <- dir.listFiles) {
if (entry.isFile) {
jar.putNextEntry(new JarEntry(prefix + entry.getName))
val input = new FileInputStream(entry)
var n = input.read(buf, 0, buf.length)
while (n >= 0) {
jar.write (buf, 0, n)
n = input.read(buf, 0, buf.length)
}
jar.closeEntry
input.close
} else {
addFromDir(entry, prefix + entry.getName + "/")
}
}
}
addFromDir(sourcePath, "")
jar.close
} catch {
case _:Error => jarFile.delete // XXX what errors to catch?
}
}
/** Read the entire contents of a file as a String. */
private def contentsOfFile(filename: String): String = {
val strbuf = new StringBuilder
val reader = new FileReader(filename)
val cbuf = new Array[Char](1024)
while(true) {
val n = reader.read(cbuf)
if (n <= 0)
return strbuf.toString
strbuf.append(cbuf, 0, n)
}
throw new Error("impossible")
}
/** Find the length of the header in the specified file, if
* there is one. The header part starts with "#!" or "::#!"
* and ends with a line that begins with "!#" or "::!#".
*/
private def headerLength(filename: String): Int = {
import java.util.regex._
val fileContents = contentsOfFile(filename)
if (!(fileContents.startsWith("#!") || fileContents.startsWith("::#!")))
return 0
val matcher =
(Pattern.compile("^(::)?!#.*(\\r|\\n|\\r\\n)", Pattern.MULTILINE)
.matcher(fileContents))
if (! matcher.find)
throw ScriptException("script file does not close its header with !# or ::!#")
return matcher.end
}
/** Split a fully qualified object name into a
* package and an unqualified object name */
private def splitObjectName(fullname: String):
(Option[String],String) =
{
val idx = fullname.lastIndexOf('.')
if (idx < 0)
(None, fullname)
else
(Some(fullname.substring(0,idx)), fullname.substring(idx+1))
}
/** Code that is added to the beginning of a script file to make
* it a complete Scala compilation unit.
*/
protected def preambleCode(objectName: String) = {
val (maybePack, objName) = splitObjectName(objectName)
val packageDecl =
maybePack match {
case Some(pack) => "package " + pack + "\n"
case None => ""
}
(packageDecl +
"object " + objName + " {\n" +
" def main(argv: Array[String]): Unit = {\n" +
" val args = argv;\n" +
" new AnyRef {\n")
}
/** Code that is added to the end of a script file to make
* it a complete Scala compilation unit.
*/
val endCode = "\n} \n} }\n"
/** Wrap a script file into a runnable object named
* scala.scripting.Main
.
*/
def wrappedScript(
objectName: String,
filename: String,
getSourceFile: PlainFile => SourceFile): SourceFile =
{
val preamble =
new BatchSourceFile("