summaryrefslogblamecommitdiff
path: root/src/compiler/scala/tools/nsc/CompileServer.scala
blob: 010d183bc8fe02b7dab94c8fa1818247659c3bfc (plain) (tree)
1
2
3
4
5
6
7
8
9
10
                            
                                

                          
       
 

                       

                                                                          
 
                                 


                                                            
 



                                                                       


                          
   
                                                  
                                                                                
 


                                           


                     
                               
 
                                     
 
                                                                          
 
                                          




                      

                                            

                                   





                                                         
                          
                                               

                                                           




                                          

                                                                       
 
                 





                                                                   
                                       
                             
                                                         
























                                                                                            
                                         











                                                                                                     
                                   




                                                 

                                                         

                                        

                                                                                      

                                   


                                                                         
           


     
                                              









                                                                              
 
                                 



                                                                  
                               
         
                                  


           


                                                  
/* NSC -- new Scala compiler
 * Copyright 2005-2009 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.reporters.{Reporter, ConsoleReporter}
import scala.tools.nsc.util.FakePos //Position
import scala.tools.util.SocketServer

/**
 *  The server part of the fsc offline compiler.  It awaits compilation
 *  commands and executes them.  It caches a compiler instance so
 *  that it can respond more quickly.
 *
 *  @author Martin Odersky
 *  @version 1.0
 */
class StandardCompileServer extends SocketServer {
  def compileSocket: CompileSocket = CompileSocket // todo: make this a lazy val

  val versionMsg = "Fast Scala compiler " +
    Properties.versionString + " -- " +
    Properties.copyrightString

  val MaxCharge = 0.8

  var shutDown: Boolean = false

  private var compiler: Global = null

  private def settingsAreCompatible(s1: Settings, s2: Settings) = s1 == s2

  private def exit(code: Int): Nothing = {
    System.err.close()
    System.out.close()
    Predef.exit(code)
  }

  private val runtime = Runtime.getRuntime()

  var reporter: ConsoleReporter = _

  /** Create a new compiler instance */
  def newGlobal(settings: Settings, reporter: Reporter) =
    new Global(settings, reporter) {
      override def inform(msg: String) = out.println(msg)
    }

  override def timeout() {
    if (!compileSocket.portFile(port).exists())
      fatal("port file no longer exists; skipping cleanup")
  }

  protected def newOfflineCompilerCommand(
    arguments: List[String],
    settings: Settings,
    error: String => Unit,
    interactive: Boolean)
  = new OfflineCompilerCommand(arguments, settings, error, interactive)

  def session() {
    System.out.println("New session" +
                       ", total memory = "+ runtime.totalMemory() +
                       ", max memory = " + runtime.maxMemory() +
                       ", free memory = " + runtime.freeMemory)
    System.out.flush()
    val password = compileSocket.getPassword(port)
    val guessedPassword = in.readLine()
    val input = in.readLine()
    if ((input ne null) && password == guessedPassword) {
      val args = input.split("\0",-1).toList
      if (args contains "-shutdown") {
        out.println("[Compile server exited]")
        shutDown = true
        return
      }
      if (args contains "-reset") {
        out.println("[Compile server was reset]")
        compiler = null
        return
      }
      def error(msg: String) {
        out.println(/*new Position*/ FakePos("fsc"),
                    msg + "\n  fsc -help  gives more information")
      }
      val command = newOfflineCompilerCommand(args, new Settings(error), error, false)

      reporter = new ConsoleReporter(command.settings, in, out) {
        // disable prompts, so that compile server cannot block
        override def displayPrompt = ()
      }

      if (command.shouldStopWithInfo) {
        reporter.info(null,
                        command.getInfoMessage(newGlobal(command.settings, reporter)), 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 compile server instance]")
                compiler = newGlobal(command.settings, reporter)
              }
              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
          }
    }
  }

  /** 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()
    compileSocket.setPort(port)
    run()
    compileSocket.deletePort(port)
    exit(0)
  }
}


object CompileServer extends StandardCompileServer