/* NSC -- new Scala compiler * Copyright 2005-2007 LAMP/EPFL * @author Martin Odersky */ // $Id$ package scala.tools.nsc import java.io.{BufferedOutputStream, File, FileOutputStream, PrintStream} import java.lang.{Runtime, System, Thread} import scala.concurrent.ops.spawn import scala.tools.nsc.doc.{DocDriver => DocGenerator} import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} import scala.tools.nsc.util.FakePos //Position import scala.tools.util.SocketServer /** The main class for NSC, a compiler for the programming * language Scala. * * @author Martin Odersky * @version 1.0 */ object CompileServer extends SocketServer { val versionMsg = "Fast Scala compiler " + Properties.versionString + " -- " + Properties.copyrightString val MaxCharge = 0.8 var shutDown: Boolean = false private var compiler: Global = null private var inSession: Boolean = false private var progress: Boolean = false private def settingsAreCompatible(s1: Settings, s2: Settings) = s1.encoding.value == s2.encoding.value && s1.classpath.value == s2.classpath.value && s1.sourcepath.value == s2.sourcepath.value && s1.outdir.value == s2.outdir.value && s1.bootclasspath.value == s2.bootclasspath.value && s1.extdirs.value == s2.extdirs.value private def exit(code: Int): Nothing = { System.err.close() System.out.close() Predef.exit(code) } private def spawnWatchDog(): Unit = spawn { while (true) { Thread.sleep(10000) if (!CompileSocket.portFile(port).exists() && !inSession) { progress = false spawn { Thread.sleep(10000) if (!progress) fatal("port file no longer exists; exiting") } } } } private val runtime = Runtime.getRuntime() var reporter: ConsoleReporter = _ def session() { System.out.println("New session" + ", total memory = "+ runtime.totalMemory() + ", max memory = " + runtime.maxMemory() + ", free memory = " + runtime.freeMemory) val password = CompileSocket.getPassword(port) val guessedPassword = in.readLine() val input = in.readLine() if ((input ne null) && password == guessedPassword) { try { inSession = true progress = true val args = input.split("\0").toList if (args contains "-shutdown") { out.println("[Scala compile server exited]") shutDown = true return } if (args contains "-reset") { out.println("[Scala compile server was reset]") compiler = null return } def error(msg: String) { reporter.error(/*new Position*/ FakePos("fsc"), msg + "\n fsc -help gives more information") } val command = new CompilerCommand(args, new Settings(error), error, false) { override val cmdName = "fsc" settings.disable(settings.prompt) settings.disable(settings.resident) new settings.BooleanSetting("-reset", "Reset compile server caches") new settings.BooleanSetting("-shutdown", "Shutdown compile server") new settings.StringSetting("-server", "hostname:portnumber", "Specify compile server socket", "") new settings.BooleanSetting("-J", "Pass directly to runtime system") } reporter = new ConsoleReporter(command.settings, in, out) { // disable prompts, so that compile server cannot block override def displayPrompt = () } if (command.settings.version.value) reporter.info(null, versionMsg, true) else if (command.settings.help.value || command.settings.Xhelp.value) { if (command.settings.help.value) reporter.info(null, command.usageMsg, true) if (command.settings.Xhelp.value) reporter.info(null, command.xusageMsg, true) } else if (command.files.isEmpty) reporter.info(null, command.usageMsg, true) else { try { if ((compiler ne null) && settingsAreCompatible(command.settings, compiler.settings)) { compiler.settings = command.settings compiler.reporter = reporter } else { if (args contains "-verbose") out.println("[Starting new Scala compile server instance]") compiler = new Global(command.settings, reporter) { override def inform(msg: String) = out.println(msg) } } val c = compiler val run = new c.Run run compile command.files } catch { case ex @ FatalError(msg) => if (command.settings.debug.value) ex.printStackTrace(out); reporter.error(null, "fatal error: " + msg) compiler = null case ex: Throwable => ex.printStackTrace(out); reporter.error(null, "fatal error (server aborted): " + ex.getMessage()) shutDown = true } reporter.printSummary() runtime.gc() if ((runtime.totalMemory() - runtime.freeMemory()).toDouble / runtime.maxMemory().toDouble > MaxCharge) compiler = null } } finally { inSession = false } } } /** A directory holding redirected output */ private val redirectDir = new File(CompileSocket.tmpDir, "output-redirects") redirectDir.mkdirs private def redirect(setter: PrintStream => Unit, filename: String) { setter( new PrintStream( new BufferedOutputStream( new FileOutputStream( new File(redirectDir, filename))))) } def main(args: Array[String]) { redirect(System.setOut, "scala-compile-server-out.log") redirect(System.setErr, "scala-compile-server-err.log") System.err.println("...starting server on socket "+port+"...") System.err.flush() spawnWatchDog() CompileSocket.setPort(port) run() CompileSocket.deletePort(port) exit(0) } }