278 lines
12 KiB
Diff
278 lines
12 KiB
Diff
From caa9ebc482969a884da5f9c9c246470811b8599d Mon Sep 17 00:00:00 2001
|
|
From: Teemu Lehtinen <teemu.t.lehtinen@aalto.fi>
|
|
Date: Wed, 20 Aug 2014 13:20:41 +0300
|
|
Subject: [PATCH] Add option -port to fsc
|
|
|
|
Option "port" limits compile server lookup and start to given port.
|
|
Normally fsc will start a compile server in a random port if no server
|
|
is yet running. This can be problematic with firewalls and/or remote
|
|
compile servers. Option "port" should not be confused with option
|
|
"server" which looks for a compile server in given host and port and
|
|
fails if such server is not found.
|
|
|
|
Automatic tests for command line user interface do not exist at all.
|
|
Thus, adding a test for one new option would require designing a whole
|
|
new testing method.
|
|
|
|
Cherry picked from 7daecd8
|
|
---
|
|
.../scala/tools/nsc/CompileClient.scala | 4 +-
|
|
.../scala/tools/nsc/CompileServer.scala | 56 ++++++++++++-------
|
|
.../scala/tools/nsc/CompileSocket.scala | 37 +++++++-----
|
|
.../tools/nsc/settings/FscSettings.scala | 4 +-
|
|
.../scala/tools/util/SocketServer.scala | 4 +-
|
|
5 files changed, 65 insertions(+), 40 deletions(-)
|
|
|
|
diff --git a/src/compiler/scala/tools/nsc/CompileClient.scala b/src/compiler/scala/tools/nsc/CompileClient.scala
|
|
index 731f6926f00..842d6ac535b 100644
|
|
--- a/src/compiler/scala/tools/nsc/CompileClient.scala
|
|
+++ b/src/compiler/scala/tools/nsc/CompileClient.scala
|
|
@@ -43,8 +43,8 @@ class StandardCompileClient extends HasCompileSocket with CompileOutputCommon {
|
|
info(vmArgs.mkString("[VM arguments: ", " ", "]"))
|
|
|
|
val socket =
|
|
- if (settings.server.value == "") compileSocket.getOrCreateSocket(vmArgs mkString " ", !shutdown)
|
|
- else Some(compileSocket.getSocket(settings.server.value))
|
|
+ if (settings.server.value == "") compileSocket.getOrCreateSocket(vmArgs mkString " ", !shutdown, settings.port.value)
|
|
+ else compileSocket.getSocket(settings.server.value)
|
|
|
|
socket match {
|
|
case Some(sock) => compileOnServer(sock, fscArgs)
|
|
diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala
|
|
index 7a0a072bb8d..6352d75686a 100644
|
|
--- a/src/compiler/scala/tools/nsc/CompileServer.scala
|
|
+++ b/src/compiler/scala/tools/nsc/CompileServer.scala
|
|
@@ -5,11 +5,13 @@
|
|
|
|
package scala.tools.nsc
|
|
|
|
-import java.io.{ BufferedOutputStream, FileOutputStream, PrintStream }
|
|
-import scala.tools.nsc.reporters.{Reporter, ConsoleReporter}
|
|
-import scala.reflect.internal.util.FakePos //Position
|
|
+import java.io.PrintStream
|
|
+
|
|
+import scala.reflect.internal.util.FakePos
|
|
+import scala.tools.nsc.io.Directory
|
|
+import scala.tools.nsc.reporters.{ConsoleReporter, Reporter}
|
|
+import scala.tools.nsc.settings.FscSettings
|
|
import scala.tools.util.SocketServer
|
|
-import settings.FscSettings
|
|
|
|
/**
|
|
* The server part of the fsc offline compiler. It awaits compilation
|
|
@@ -19,7 +21,7 @@ import settings.FscSettings
|
|
* @author Martin Odersky
|
|
* @version 1.0
|
|
*/
|
|
-class StandardCompileServer extends SocketServer {
|
|
+class StandardCompileServer(fixPort: Int = 0) extends SocketServer(fixPort) {
|
|
lazy val compileSocket: CompileSocket = CompileSocket
|
|
|
|
private var compiler: Global = null
|
|
@@ -34,7 +36,7 @@ class StandardCompileServer extends SocketServer {
|
|
val MaxCharge = 0.8
|
|
|
|
private val runtime = Runtime.getRuntime()
|
|
- import runtime.{ totalMemory, freeMemory, maxMemory }
|
|
+ import runtime.{freeMemory, maxMemory, totalMemory}
|
|
|
|
/** Create a new compiler instance */
|
|
def newGlobal(settings: Settings, reporter: Reporter) =
|
|
@@ -170,16 +172,16 @@ class StandardCompileServer extends SocketServer {
|
|
}
|
|
|
|
|
|
-object CompileServer extends StandardCompileServer {
|
|
+object CompileServer {
|
|
/** A directory holding redirected output */
|
|
- private lazy val redirectDir = (compileSocket.tmpDir / "output-redirects").createDirectory()
|
|
+ //private lazy val redirectDir = (compileSocket.tmpDir / "output-redirects").createDirectory()
|
|
|
|
- private def createRedirect(filename: String) =
|
|
- new PrintStream((redirectDir / filename).createFile().bufferedOutput())
|
|
+ private def createRedirect(dir: Directory, filename: String) =
|
|
+ new PrintStream((dir / filename).createFile().bufferedOutput())
|
|
|
|
- def main(args: Array[String]) =
|
|
+ def main(args: Array[String]) =
|
|
execute(() => (), args)
|
|
-
|
|
+
|
|
/**
|
|
* Used for internal testing. The callback is called upon
|
|
* server start, notifying the caller that the server is
|
|
@@ -191,21 +193,33 @@ object CompileServer extends StandardCompileServer {
|
|
*/
|
|
def execute(startupCallback : () => Unit, args: Array[String]) {
|
|
val debug = args contains "-v"
|
|
+ var port = 0
|
|
+
|
|
+ val i = args.indexOf("-p")
|
|
+ if (i >= 0 && args.length > i + 1) {
|
|
+ scala.util.control.Exception.ignoring(classOf[NumberFormatException]) {
|
|
+ port = args(i + 1).toInt
|
|
+ }
|
|
+ }
|
|
+
|
|
+ // Create instance rather than extend to pass a port parameter.
|
|
+ val server = new StandardCompileServer(port)
|
|
+ val redirectDir = (server.compileSocket.tmpDir / "output-redirects").createDirectory()
|
|
|
|
if (debug) {
|
|
- echo("Starting CompileServer on port " + port)
|
|
- echo("Redirect dir is " + redirectDir)
|
|
+ server.echo("Starting CompileServer on port " + server.port)
|
|
+ server.echo("Redirect dir is " + redirectDir)
|
|
}
|
|
|
|
- Console.withErr(createRedirect("scala-compile-server-err.log")) {
|
|
- Console.withOut(createRedirect("scala-compile-server-out.log")) {
|
|
- Console.err.println("...starting server on socket "+port+"...")
|
|
+ Console.withErr(createRedirect(redirectDir, "scala-compile-server-err.log")) {
|
|
+ Console.withOut(createRedirect(redirectDir, "scala-compile-server-out.log")) {
|
|
+ Console.err.println("...starting server on socket "+server.port+"...")
|
|
Console.err.flush()
|
|
- compileSocket setPort port
|
|
+ server.compileSocket setPort server.port
|
|
startupCallback()
|
|
- run()
|
|
-
|
|
- compileSocket deletePort port
|
|
+ server.run()
|
|
+
|
|
+ server.compileSocket deletePort server.port
|
|
}
|
|
}
|
|
}
|
|
diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala
|
|
index 4051bda9144..f5039b8303f 100644
|
|
--- a/src/compiler/scala/tools/nsc/CompileSocket.scala
|
|
+++ b/src/compiler/scala/tools/nsc/CompileSocket.scala
|
|
@@ -5,16 +5,13 @@
|
|
|
|
package scala.tools.nsc
|
|
|
|
-import java.io.{ IOException, FileNotFoundException, PrintWriter, FileOutputStream }
|
|
-import java.io.{ BufferedReader, FileReader }
|
|
-import java.util.regex.Pattern
|
|
-import java.net._
|
|
+import java.io.FileNotFoundException
|
|
import java.security.SecureRandom
|
|
-import io.{ File, Path, Directory, Socket }
|
|
-import scala.util.control.Exception.catching
|
|
-import scala.tools.util.CompileOutputCommon
|
|
+
|
|
import scala.reflect.internal.util.StringOps.splitWhere
|
|
import scala.sys.process._
|
|
+import scala.tools.nsc.io.{File, Path, Socket}
|
|
+import scala.tools.util.CompileOutputCommon
|
|
|
|
trait HasCompileSocket {
|
|
def compileSocket: CompileSocket
|
|
@@ -50,6 +47,9 @@ class CompileSocket extends CompileOutputCommon {
|
|
protected lazy val compileClient: StandardCompileClient = CompileClient
|
|
def verbose = compileClient.verbose
|
|
|
|
+ /* Fixes the port where to start the server, 0 yields some free port */
|
|
+ var fixPort = 0
|
|
+
|
|
/** The prefix of the port identification file, which is followed
|
|
* by the port number.
|
|
*/
|
|
@@ -67,7 +67,7 @@ class CompileSocket extends CompileOutputCommon {
|
|
|
|
/** The class name of the scala compile server */
|
|
protected val serverClass = "scala.tools.nsc.CompileServer"
|
|
- protected def serverClassArgs = if (verbose) List("-v") else Nil // debug
|
|
+ protected def serverClassArgs = (if (verbose) List("-v") else Nil) ::: (if (fixPort > 0) List("-p", fixPort.toString) else Nil)
|
|
|
|
/** A temporary directory to use */
|
|
val tmpDir = {
|
|
@@ -107,9 +107,14 @@ class CompileSocket extends CompileOutputCommon {
|
|
def portFile(port: Int) = portsDir / File(port.toString)
|
|
|
|
/** Poll for a server port number; return -1 if none exists yet */
|
|
- private def pollPort(): Int = portsDir.list.toList match {
|
|
+ private def pollPort(): Int = if (fixPort > 0) {
|
|
+ if (portsDir.list.toList.exists(_.name == fixPort.toString)) fixPort else -1
|
|
+ } else portsDir.list.toList match {
|
|
case Nil => -1
|
|
- case x :: xs => try x.name.toInt finally xs foreach (_.delete())
|
|
+ case x :: xs => try x.name.toInt catch {
|
|
+ case e: Exception => x.delete()
|
|
+ throw e
|
|
+ }
|
|
}
|
|
|
|
/** Get the port number to which a scala compile server is connected;
|
|
@@ -155,7 +160,8 @@ class CompileSocket extends CompileOutputCommon {
|
|
* create a new daemon if necessary. Returns None if the connection
|
|
* cannot be established.
|
|
*/
|
|
- def getOrCreateSocket(vmArgs: String, create: Boolean = true): Option[Socket] = {
|
|
+ def getOrCreateSocket(vmArgs: String, create: Boolean = true, fixedPort: Int = 0): Option[Socket] = {
|
|
+ fixPort = fixedPort
|
|
val maxMillis = 10 * 1000 // try for 10 seconds
|
|
val retryDelay = 50
|
|
val maxAttempts = maxMillis / retryDelay
|
|
@@ -189,13 +195,16 @@ class CompileSocket extends CompileOutputCommon {
|
|
try { Some(x.toInt) }
|
|
catch { case _: NumberFormatException => None }
|
|
|
|
- def getSocket(serverAdr: String): Socket = (
|
|
+ def getSocket(serverAdr: String): Option[Socket] = (
|
|
for ((name, portStr) <- splitWhere(serverAdr, _ == ':', true) ; port <- parseInt(portStr)) yield
|
|
getSocket(name, port)
|
|
) getOrElse fatal("Malformed server address: %s; exiting" format serverAdr)
|
|
|
|
- def getSocket(hostName: String, port: Int): Socket =
|
|
- Socket(hostName, port).opt getOrElse fatal("Unable to establish connection to server %s:%d; exiting".format(hostName, port))
|
|
+ def getSocket(hostName: String, port: Int): Option[Socket] = {
|
|
+ val sock = Socket(hostName, port).opt
|
|
+ if (sock.isEmpty) warn("Unable to establish connection to server %s:%d".format(hostName, port))
|
|
+ sock
|
|
+ }
|
|
|
|
def getPassword(port: Int): String = {
|
|
val ff = portFile(port)
|
|
diff --git a/src/compiler/scala/tools/nsc/settings/FscSettings.scala b/src/compiler/scala/tools/nsc/settings/FscSettings.scala
|
|
index 5c852ae07c1..f5f971d697e 100644
|
|
--- a/src/compiler/scala/tools/nsc/settings/FscSettings.scala
|
|
+++ b/src/compiler/scala/tools/nsc/settings/FscSettings.scala
|
|
@@ -22,13 +22,15 @@ class FscSettings(error: String => Unit) extends Settings(error) {
|
|
val reset = BooleanSetting("-reset", "Reset compile server caches")
|
|
val shutdown = BooleanSetting("-shutdown", "Shutdown compile server")
|
|
val server = StringSetting ("-server", "hostname:portnumber", "Specify compile server socket", "")
|
|
+ val port = IntSetting ("-port", "Search and start compile server in given port only",
|
|
+ 0, Some((0, Int.MaxValue)), (_: String) => None)
|
|
val preferIPv4 = BooleanSetting("-ipv4", "Use IPv4 rather than IPv6 for the server socket")
|
|
val idleMins = IntSetting ("-max-idle", "Set idle timeout in minutes for fsc (use 0 for no timeout)",
|
|
30, Some((0, Int.MaxValue)), (_: String) => None)
|
|
|
|
// For improved help output, separating fsc options from the others.
|
|
def fscSpecific = Set[Settings#Setting](
|
|
- currentDir, reset, shutdown, server, preferIPv4, idleMins
|
|
+ currentDir, reset, shutdown, server, port, preferIPv4, idleMins
|
|
)
|
|
val isFscSpecific: String => Boolean = fscSpecific map (_.name)
|
|
|
|
diff --git a/src/compiler/scala/tools/util/SocketServer.scala b/src/compiler/scala/tools/util/SocketServer.scala
|
|
index 1b06ce2ff2e..edbc7ecc554 100644
|
|
--- a/src/compiler/scala/tools/util/SocketServer.scala
|
|
+++ b/src/compiler/scala/tools/util/SocketServer.scala
|
|
@@ -27,12 +27,12 @@ trait CompileOutputCommon {
|
|
* @author Martin Odersky
|
|
* @version 1.0
|
|
*/
|
|
-abstract class SocketServer extends CompileOutputCommon {
|
|
+abstract class SocketServer(fixPort: Int = 0) extends CompileOutputCommon {
|
|
def shutdown: Boolean
|
|
def session(): Unit
|
|
def timeout(): Unit = () // called after a timeout is detected for subclasses to cleanup
|
|
// a hook for subclasses
|
|
- protected def createServerSocket(): ServerSocket = new ServerSocket(0)
|
|
+ protected def createServerSocket(): ServerSocket = new ServerSocket(fixPort)
|
|
|
|
var in: BufferedReader = _
|
|
var out: PrintWriter = _
|