/* 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} 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.ConsoleReporter import scala.tools.nsc.util.{CompoundSourceFile, 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" /** 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 new Error("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)) } /** Wrap a script file into a runnable object named * scala.scripting.Main. */ def wrappedScript( objectName: String, filename: String, getSourceFile: PlainFile => SourceFile): SourceFile = { val (maybePack, objName) = splitObjectName(objectName) val packageDecl = maybePack match { case Some(pack) => "package " + pack + "\n" case None => "" } val preamble = new SourceFile("