compilesocket.scala

来自「JAVA 语言的函数式编程扩展」· SCALA 代码 · 共 269 行

SCALA
269
字号
/* NSC -- new Scala compiler * Copyright 2005-2007 LAMP/EPFL * @author  Martin Odersky */// $Id: CompileSocket.scala 14416 2008-03-19 01:17:25Z mihaylov $package scala.tools.nscimport java.lang.{Thread, System, Runtime}import java.lang.NumberFormatExceptionimport java.io.{File, IOException, PrintWriter, FileOutputStream}import java.io.{BufferedReader, FileReader}import java.util.regex.Patternimport java.net._/** This class manages sockets for the fsc offline compiler.  */class CompileSocket {  protected def compileClient: StandardCompileClient = CompileClient //todo: lazy val  /** The prefix of the port identification file, which is followed   *  by the port number.   */  protected def dirName = "scalac-compile-server-port" //todo: lazy val  protected def cmdName = Properties.cmdName //todo: lazy val  /** The vm part of the command to start a new scala compile server */  protected val vmCommand =    Properties.scalaHome match {      case null =>        cmdName      case dirname =>        val trial = new File(new File(dirname, "bin"), cmdName)        if (trial.canRead)          trial.getPath        else          cmdName    }  /** The class name of the scala compile server */  protected val serverClass = "scala.tools.nsc.CompileServer"      /** A regular expression for checking compiler output for errors */  val errorRegex = ".*(errors? found|don't know|bad option).*"      /** A Pattern object for checking compiler output for errors */  val errorPattern = Pattern.compile(errorRegex)  protected def error(msg: String) = System.err.println(msg)      protected def fatal(msg: String) = {    error(msg)    exit(1)  }  protected def info(msg: String) =    if (compileClient.verbose) System.out.println(msg)  /** A temporary directory to use */  val tmpDir = {    val totry = List(        ("scala.home", List("var", "scala-devel")),        ("user.home", List("tmp")),        ("java.io.tmpdir", Nil))    /** Expand a property-extensions pair into a complete File object */    def expand(trial: (String, List[String])): Option[File] = {      val (topdirProp, extensions) = trial      val topdir = System.getProperty(topdirProp)      if (topdir eq null)        return None      val fulldir =        extensions.foldLeft[File](new File(topdir))(            (dir,ext) => new File(dir, ext))      Some(fulldir)    }    /** Try to create directory f, and then see if it can     *  be written into. */    def isDirWritable(f: File): Boolean = {      f.mkdirs()      f.isDirectory && f.canWrite    }    val potentials =      for {        trial <- totry        val expanded = expand(trial)        if !expanded.isEmpty        if isDirWritable(expanded.get)      }      yield expanded.get    if (potentials.isEmpty)      fatal("Could not find a directory for temporary files")    else {      val d = potentials.head      d.mkdirs      info("[Temp directory: " + d + "]")      d    }  }    /* A directory holding port identification files */  val portsDir =  new File(tmpDir, dirName)  portsDir.mkdirs  /** Maximum number of polls for an available port */  private val MaxAttempts = 100  /** Time (in ms) to sleep between two polls */  private val sleepTime = 20  /** The command which starts the compile server, given vm arguments.    *    *  @param vmArgs  the argument string to be passed to the java or scala command    *                 the string must be either empty or start with a ' '.    */  private def serverCommand(vmArgs: String): String =    vmCommand + vmArgs + " " + serverClass  /** Start a new server; returns true iff it succeeds */  private def startNewServer(vmArgs: String) {    val cmd = serverCommand(vmArgs)    info("[Executed command: " + cmd + "]")    try {      Runtime.getRuntime().exec(cmd)//      val exitVal = proc.waitFor()//      info("[Exit value: " + exitVal + "]")    } catch {      case ex: IOException =>        fatal("Cannot start compilation daemon." +              "\ntried command: " + cmd)    }  }  /** The port identification file */  def portFile(port: Int) = new File(portsDir, port.toString())  /** Poll for a server port number; return -1 if none exists yet */  private def pollPort(): Int = {    val hits = portsDir.listFiles()    if (hits.length == 0) -1    else      try {        for (i <- 1 until hits.length) hits(i).delete()        hits(0).getName.toInt      } catch {        case ex: NumberFormatException =>          fatal(ex.toString() +                "\nbad file in temp directory: " +                hits(0).getAbsolutePath() +                "\nplease remove the file and try again")      }  }  /** Get the port number to which a scala compile server is connected;   *  If no server is running yet, then create one.   */  def getPort(vmArgs: String): Int = {    var attempts = 0    var port = pollPort()    if (port < 0)      startNewServer(vmArgs)    while (port < 0 && attempts < MaxAttempts) {      attempts += 1      Thread.sleep(sleepTime)      port = pollPort()    }    info("[Port number: " + port + "]")    if (port < 0)      fatal("Could not connect to compilation daemon.")    port  }  /** Set the port number to which a scala compile server is connected */  def setPort(port: Int) {    try {      val f = new PrintWriter(new FileOutputStream(portFile(port)))      f.println(new java.security.SecureRandom().nextInt.toString)      f.close()    } catch {      case ex: /*FileNotFound+Security*/Exception =>        fatal("Cannot create file: " +              portFile(port).getAbsolutePath())    }  }  /** Delete the port number to which a scala compile server was connected */  def deletePort(port: Int) { portFile(port).delete() }  /** Get a socket connected to a daemon.  If create is true, then    * create a new daemon if necessary.  Returns null if the connection    * cannot be established.    */  def getOrCreateSocket(vmArgs: String, create: Boolean): Socket = {    val nAttempts = 49  // try for about 5 seconds    def getsock(attempts: Int): Socket =      if (attempts == 0) {        error("Unable to establish connection to compilation daemon")        null      } else {        val port = if(create) getPort(vmArgs) else pollPort()        if(port < 0) return null        val hostName = InetAddress.getLocalHost().getHostName()        try {          val result = new Socket(hostName, port)          info("[Connected to compilation daemon at port " + port + "]")          result        } catch {          case e: /*IO+Security*/Exception =>            info(e.toString)            info("[Connecting to compilation daemon at port "  +                 port + " failed; re-trying...]")                        if (attempts % 2 == 0)               portFile(port).delete // 50% chance to stop trying on this port                          Thread.sleep(100) // delay before retrying                        getsock(attempts - 1)        }      }    getsock(nAttempts)  }    /** Same as getOrCreateSocket(vmArgs, true). */  def getOrCreateSocket(vmArgs: String): Socket =    getOrCreateSocket(vmArgs, true)  def getSocket(serverAdr: String): Socket = {    val cpos = serverAdr indexOf ':'    if (cpos < 0)      fatal("Malformed server address: " + serverAdr + "; exiting")    else {      val hostName = serverAdr.substring(0, cpos)      val port = try {        serverAdr.substring(cpos+1).toInt      } catch {        case ex: Throwable =>          fatal("Malformed server address: " + serverAdr + "; exiting")      }      getSocket(hostName, port)    }  }  def getSocket(hostName: String, port: Int): Socket =    try {      new Socket(hostName, port)    } catch {      case e: /*IO+Security*/Exception =>        fatal("Unable to establish connection to server " +              hostName + ":" + port + "; exiting")    }  def getPassword(port: Int): String = {    val f = new BufferedReader(new FileReader(portFile(port)))    val result = f.readLine()    f.close()    result  }}object CompileSocket extends CompileSocket

⌨️ 快捷键说明

复制代码Ctrl + C
搜索代码Ctrl + F
全屏模式F11
增大字号Ctrl + =
减小字号Ctrl + -
显示快捷键?