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

                          
       
 

                       


                                                                          
                                 
                                                      


                                                            


                                                          


                          


                                           


                                           


                     
                               
 
                                     

                                        
 







                                                                 
                                          




                      
                                             





                                                                 

                                                        

         




                                            

                                   
                 



                                                                   

                                                  
                             
                                                         


                        
                                           







                                                         
                
         
                                

                                                                       
         
                                                                                    








                                                                                           
 

                                                                   
                                         

         

                                               



                                                                                        


                                                     
               
                                                                                                   


                                                  
                                           
                                                                           
                                                                 

                                                                   
             









                                                         
                                      






                                                                                      
         

                         



       
                                              
                                                                              

                    
                                                                       



                                 
                                               
   
 
                                 










                                                                  
/* 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<flag>", "Pass <flag> 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)
  }
}