summaryrefslogtreecommitdiff
diff options
context:
space:
mode:
authorPaul Phillips <paulp@improving.org>2011-02-08 08:47:29 +0000
committerPaul Phillips <paulp@improving.org>2011-02-08 08:47:29 +0000
commit3467ad57e4c2b56cdd02ce0c75af4716b5631a10 (patch)
treeaf5d632276e79a449e9971e94c25bec0c1fc73e4
parentc89ea6e3ae82d9b6bacda25dceb74a958d2fa4f6 (diff)
downloadscala-3467ad57e4c2b56cdd02ce0c75af4716b5631a10.tar.gz
scala-3467ad57e4c2b56cdd02ce0c75af4716b5631a10.tar.bz2
scala-3467ad57e4c2b56cdd02ce0c75af4716b5631a10.zip
Working on fsc.
for me anyway, with this commit scripts will occasionally reuse a compiler instance, instead of never. Since any tests I write will fail on platforms which aren't mine, there are no tests. I might have to start a platform-specific testing area to break some ice around these huge untested zones. No review.
-rw-r--r--src/compiler/scala/tools/nsc/CompileClient.scala92
-rw-r--r--src/compiler/scala/tools/nsc/CompileServer.scala96
-rw-r--r--src/compiler/scala/tools/nsc/CompileSocket.scala4
-rw-r--r--src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala15
-rw-r--r--src/compiler/scala/tools/nsc/ScriptRunner.scala6
-rw-r--r--src/compiler/scala/tools/nsc/settings/AbsSettings.scala2
-rw-r--r--src/compiler/scala/tools/nsc/settings/FscSettings.scala29
-rw-r--r--src/compiler/scala/tools/nsc/util/ClassPath.scala3
-rw-r--r--src/compiler/scala/tools/util/SocketServer.scala23
9 files changed, 136 insertions, 134 deletions
diff --git a/src/compiler/scala/tools/nsc/CompileClient.scala b/src/compiler/scala/tools/nsc/CompileClient.scala
index 41b96bfcdd..3102a2ae44 100644
--- a/src/compiler/scala/tools/nsc/CompileClient.scala
+++ b/src/compiler/scala/tools/nsc/CompileClient.scala
@@ -9,87 +9,44 @@ import java.io.{ BufferedReader, File, InputStreamReader, PrintWriter }
import Properties.fileEndings
import scala.tools.util.PathResolver
import io.Path
-import util.ClassPath
+import settings.FscSettings
/** The client part of the fsc offline compiler. Instead of compiling
* things itself, it send requests to a CompileServer.
*/
-class StandardCompileClient extends CompileSocketShared {
+class StandardCompileClient extends HasCompileSocket {
lazy val compileSocket: CompileSocket = CompileSocket
val versionMsg = "Fast " + Properties.versionMsg
- var verbose = false
- var version = false
- var shutdown = false
-
- /** Convert a filename to an absolute path */
- def absFileName(path: String) = new File(path).getAbsolutePath()
-
- /** Convert a sequence of filenames, separated by <code>File.pathSeparator</code>,
- * into absolute filenames.
- */
- def absFileNames(paths: String) = ClassPath.map(paths, absFileName)
-
- protected def normalize(args: Array[String]): (String, String) = {
- var i = 0
- val vmArgs = new StringBuilder
- var serverAdr = ""
-
- while (i < args.length) {
- val arg = args(i)
- if (fileEndings exists(arg endsWith _)) {
- args(i) = Path(arg).toAbsolute.path
- } else if (arg startsWith "-J") {
- //see http://java.sun.com/j2se/1.5.0/docs/tooldocs/solaris/javac.html#J
- vmArgs append " "+arg.substring(2)
- args(i) = ""
- } else if (arg == "-verbose") {
- verbose = true
- } else if (arg == "-version") {
- version = true
- } else if (arg == "-shutdown") {
- shutdown = true
- }
- i += 1
-
- if (i < args.length) {
- arg match {
- case "-classpath" | "-sourcepath" | "-bootclasspath" | "-extdirs" | "-d" =>
- args(i) = PathResolver.makeAbsolute(args(i))
- i += 1
- case "-server" =>
- serverAdr = args(i)
- args(i-1) = ""
- args(i) = ""
- case _ =>
- }
- }
- }
- (vmArgs.toString, serverAdr)
- }
-
- // used by class ant.FastScalac to skip exit statement in Ant.
- def main0(args0: Array[String]): Int = {
- val args = if (args0 contains "-d") args0 else Array("-d", ".") ++ args0
- val (vmArgs, serverAdr) = normalize(args)
-
- if (version) {
+ var verbose = false
+
+ def main0(argsIn: Array[String]): Int = {
+ // TODO: put -J -and -D options back. Right now they are lost
+ // because bash parses them out and they don't arrive.
+ val (vmArgs, fscArgs) = (Nil, argsIn.toList)
+ val settings = new FscSettings
+ val command = new CompilerCommand(fscArgs, settings)
+ verbose = settings.verbose.value
+ val shutdown = settings.shutdown.value
+
+ if (settings.version.value) {
Console println versionMsg
return 0
}
if (verbose) {
- Console println args.mkString("[Server arguments: ", " ", "]")
- Console println "[VM arguments: %s]".format(vmArgs)
+ Console println fscArgs.mkString("[Given arguments: ", " ", "]")
+ Console println vmArgs.mkString("[VM arguments: ", " ", "]")
}
val socket =
- if (serverAdr == "") compileSocket.getOrCreateSocket(vmArgs, !shutdown)
- else Some(compileSocket.getSocket(serverAdr))
+ if (settings.server.value == "") compileSocket.getOrCreateSocket(vmArgs mkString " ", !shutdown)
+ else Some(compileSocket.getSocket(settings.server.value))
val success = socket match {
- case Some(sock) => fscCompile(sock, args)
+ case Some(sock) => compileOnServer(sock, fscArgs)
case _ =>
Console.println(
- if (shutdown) "[No compilation server running.]" else "Compilation failed."
+ if (shutdown) "[No compilation server running.]"
+ else "Compilation failed."
)
shutdown
}
@@ -98,6 +55,9 @@ class StandardCompileClient extends CompileSocketShared {
}
object CompileClient extends StandardCompileClient {
- def main(args: Array[String]): Unit =
- sys.exit(try main0(args) catch { case e: Exception => 1 })
+ def main(args: Array[String]): Unit = sys exit {
+ try main0(args)
+ catch { case _: Exception => 1 }
+ }
}
+
diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala
index 5296432519..e33545d3c9 100644
--- a/src/compiler/scala/tools/nsc/CompileServer.scala
+++ b/src/compiler/scala/tools/nsc/CompileServer.scala
@@ -6,11 +6,10 @@
package scala.tools.nsc
import java.io.{ BufferedOutputStream, FileOutputStream, PrintStream, File => JFile }
-import io.File
-
import scala.tools.nsc.reporters.{Reporter, ConsoleReporter}
import scala.tools.nsc.util.FakePos //Position
import scala.tools.util.SocketServer
+import settings.FscSettings
/**
* The server part of the fsc offline compiler. It awaits compilation
@@ -29,9 +28,9 @@ class StandardCompileServer extends SocketServer {
val MaxCharge = 0.8
- var shutDown: Boolean = false
-
private var compiler: Global = null
+ var reporter: ConsoleReporter = _
+ var shutdown = false
private def exit(code: Int): Nothing = {
System.err.close()
@@ -42,8 +41,6 @@ class StandardCompileServer extends SocketServer {
private val runtime = Runtime.getRuntime()
import runtime.{ totalMemory, freeMemory, maxMemory }
- var reporter: ConsoleReporter = _
-
/** Create a new compiler instance */
def newGlobal(settings: Settings, reporter: Reporter) =
new Global(settings, reporter) {
@@ -69,11 +66,29 @@ class StandardCompileServer extends SocketServer {
protected def newOfflineCompilerCommand(arguments: List[String], settings: Settings) =
new OfflineCompilerCommand(arguments, settings)
+ /** Problematically, Settings are only considered equal if every setting
+ * is exactly equal. In fsc this immediately breaks down because the randomly
+ * chosen temporary outdirs differ between client and server. Among other
+ * things. Long term we could use a meaningful equality; short term I'm just
+ * ignoring options which I can see causing a new compiler instance every time
+ * and which do not interestingly influence compilation products.
+ */
+ def unequalSettings(s1: Settings, s2: Settings): Set[Settings#Setting] = {
+ val ignoreSettings = Set("-d", "-encoding", "-verbose")
+ def trim (s: Settings): Set[Settings#Setting] = (
+ s.userSetSettings.toSet[Settings#Setting] filterNot (ss => ignoreSettings exists (ss respondsTo _))
+ )
+ val ss1 = trim(s1)
+ val ss2 = trim(s2)
+
+ (ss1 union ss2) -- (ss1 intersect ss2)
+ }
+
def session() {
printMemoryStats()
- val password = compileSocket getPassword port
+ val password = compileSocket getPassword port
val guessedPassword = in.readLine()
- val input = in.readLine()
+ val input = in.readLine()
def fscError(msg: String): Unit = out println (
FakePos("fsc"),
@@ -83,13 +98,17 @@ class StandardCompileServer extends SocketServer {
return
val args = input.split("\0", -1).toList
- val command = newOfflineCompilerCommand(args, new Settings(fscError))
-
- if (command.fscShutdown.value) {
- shutDown = true
+ val settings = new FscSettings(fscError)
+ def logVerbose(msg: String) =
+ if (settings.verbose.value)
+ out println msg
+
+ val command = newOfflineCompilerCommand(args, settings)
+ if (settings.shutdown.value) {
+ shutdown = true
return out.println("[Compile server exited]")
}
- if (command.fscReset.value) {
+ if (settings.reset.value) {
compiler = null
return out.println("[Compile server was reset]")
}
@@ -98,45 +117,48 @@ class StandardCompileServer extends SocketServer {
// disable prompts, so that compile server cannot block
override def displayPrompt = ()
}
+ def isCompilerReusable: Boolean = {
+ if (compiler == null) {
+ logVerbose("[Creating compiler instance for compile server.]")
+ return false
+ }
+ val unequal = unequalSettings(command.settings, compiler.settings)
+ if (unequal.nonEmpty) {
+ logVerbose("[Replacing compiler with new instance because settings are unequal.]")
+ logVerbose("[Asymmetric settings: " + unequal.mkString(", ") + "]")
+ }
+ unequal.isEmpty
+ }
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 != null && command.settings == compiler.settings) {
- compiler.settings = command.settings
- compiler.reporter = reporter
- }
- else {
- if (command.verbose) {
- val reason = if (compiler == null) "compiler is null" else "settings not equal"
- out.println("[Starting new compile server instance because %s]".format(reason))
- }
- compiler = newGlobal(command.settings, reporter)
- }
- val c = compiler
- val run = new c.Run()
- run compile command.files
+ if (isCompilerReusable) {
+ compiler.settings = command.settings
+ compiler.reporter = reporter
}
+ else {
+ compiler = newGlobal(command.settings, reporter)
+ }
+ val c = compiler
+ val run = new c.Run()
+ try run compile command.files
catch {
case ex @ FatalError(msg) =>
- if (command.debug)
- 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
+ case ex =>
+ shutdown = true
+ throw ex
}
}
-
reporter.printSummary()
- if (isMemoryFullEnough)
+ if (isMemoryFullEnough) {
+ logVerbose("Nulling out compiler due to memory utilization.")
compiler = null
+ }
}
}
diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala
index 1e4b37b3bc..4f71a3da87 100644
--- a/src/compiler/scala/tools/nsc/CompileSocket.scala
+++ b/src/compiler/scala/tools/nsc/CompileSocket.scala
@@ -16,9 +16,9 @@ import scala.util.control.Exception.catching
import scala.tools.util.StringOps.splitWhere
import scala.sys.process._
-trait CompileSocketShared {
+trait HasCompileSocket {
def compileSocket: CompileSocket
- def fscCompile(sock: Socket, args: Seq[String]): Boolean = {
+ def compileOnServer(sock: Socket, args: Seq[String]): Boolean = {
var noErrors = true
sock.applyReaderAndWriter { (in, out) =>
diff --git a/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala b/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala
index c04ce45d6c..786bcb1eb5 100644
--- a/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala
+++ b/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala
@@ -9,19 +9,6 @@ package scala.tools.nsc
*
* @author Martin Odersky and Lex Spoon
*/
-class OfflineCompilerCommand(arguments: List[String], _settings: Settings)
- extends CompilerCommand(arguments, _settings)
-{
+class OfflineCompilerCommand(arguments: List[String], settings: Settings) extends CompilerCommand(arguments, settings) {
override def cmdName = "fsc"
- import settings._
-
- disable(prompt)
- disable(resident)
-
- def verbose = settings.verbose.value
- def debug = settings.debug.value
-
- val fscReset = BooleanSetting("-reset", "Reset compile server caches")
- val fscShutdown = BooleanSetting("-shutdown", "Shutdown compile server")
- val fscServer = StringSetting ("-server", "hostname:portnumber", "Specify compile server socket", "")
}
diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala
index fcfb827089..569dcfa01e 100644
--- a/src/compiler/scala/tools/nsc/ScriptRunner.scala
+++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala
@@ -47,7 +47,7 @@ import util.Exceptional.unwrap
* @todo It would be better if error output went to stderr instead
* of stdout...
*/
-class ScriptRunner extends CompileSocketShared {
+class ScriptRunner extends HasCompileSocket {
lazy val compileSocket = CompileSocket
/* While I'm chasing down the fsc and script bugs. */
@@ -135,7 +135,7 @@ class ScriptRunner extends CompileSocketShared {
val compArgs = coreCompArgs ++ List("-Xscript", scriptMain(settings), scriptFile)
CompileSocket getOrCreateSocket "" match {
- case Some(sock) => fscCompile(sock, compArgs)
+ case Some(sock) => compileOnServer(sock, compArgs)
case _ => false
}
}
@@ -276,4 +276,4 @@ class ScriptRunner extends CompileSocketShared {
}
}
-object ScriptRunner extends ScriptRunner { } \ No newline at end of file
+object ScriptRunner extends ScriptRunner { }
diff --git a/src/compiler/scala/tools/nsc/settings/AbsSettings.scala b/src/compiler/scala/tools/nsc/settings/AbsSettings.scala
index f09bc3edd1..a797cefef3 100644
--- a/src/compiler/scala/tools/nsc/settings/AbsSettings.scala
+++ b/src/compiler/scala/tools/nsc/settings/AbsSettings.scala
@@ -34,7 +34,7 @@ trait AbsSettings {
// two AbsSettings objects are equal if their visible settings are equal.
override def hashCode() = visibleSettings.hashCode
override def equals(that: Any) = that match {
- case s: AbsSettings => this.visibleSettings == s.visibleSettings
+ case s: AbsSettings => this.userSetSettings == s.userSetSettings
case _ => false
}
override def toString() = "Settings {\n%s}\n" format (userSetSettings map (" " + _ + "\n") mkString)
diff --git a/src/compiler/scala/tools/nsc/settings/FscSettings.scala b/src/compiler/scala/tools/nsc/settings/FscSettings.scala
new file mode 100644
index 0000000000..b7f0c553f6
--- /dev/null
+++ b/src/compiler/scala/tools/nsc/settings/FscSettings.scala
@@ -0,0 +1,29 @@
+/* NSC -- new Scala compiler
+ * Copyright 2005-2011 LAMP/EPFL
+ * @author Paul Phillips
+ */
+
+package scala.tools
+package nsc
+package settings
+
+import util.ClassPath
+
+class FscSettings(error: String => Unit) extends Settings(error) {
+ outer =>
+
+ def this() = this(Console.println)
+
+ 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", "")
+
+ disable(prompt)
+ disable(resident)
+
+ // Make all paths absolute since we may be going from client to server
+ userSetSettings foreach {
+ case x: PathSetting => x.value = ClassPath.makeAbsolute(x.value)
+ case _ => ()
+ }
+}
diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala
index 0a8c54f297..d64eb78cc5 100644
--- a/src/compiler/scala/tools/nsc/util/ClassPath.scala
+++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala
@@ -79,6 +79,9 @@ object ClassPath {
/** Split the classpath and map them into Paths */
def toPaths(cp: String): List[Path] = split(cp) map (x => Path(x).toAbsolute)
+ /** Make all classpath components absolute. */
+ def makeAbsolute(cp: String): String = fromPaths(toPaths(cp): _*)
+
/** Join the paths as a classpath */
def fromPaths(paths: Path*): String = join(paths map (_.path): _*)
diff --git a/src/compiler/scala/tools/util/SocketServer.scala b/src/compiler/scala/tools/util/SocketServer.scala
index 5f5cb28c4c..57ebf96656 100644
--- a/src/compiler/scala/tools/util/SocketServer.scala
+++ b/src/compiler/scala/tools/util/SocketServer.scala
@@ -32,14 +32,14 @@ abstract class SocketServer {
// therefore unable to contact this server instance,
// the process will just eventually terminate by itself.
def fscIdleMinutes = {
- sys.props("scala.fsc.idle.minutes") match {
+ sys.props("scala.config.fsc.idle-minutes") match {
case null => 30
case str => try str.toInt catch { case _: Exception => 30 }
}
}
def fscIdleMillis = fscIdleMinutes * 60 * 1000
- def shutDown: Boolean
+ def shutdown: Boolean
def session()
var out: PrintWriter = _
@@ -68,14 +68,15 @@ abstract class SocketServer {
// @todo: this is going to be a prime candidate for ARM
def doSession(clientSocket: Socket) = {
out = new PrintWriter(clientSocket.getOutputStream(), true)
- in = bufferedReader(clientSocket)
+ in = bufferedReader(clientSocket)
val bufout = bufferedOutput(clientSocket)
- scala.Console.withOut(bufout) { session() }
-
- bufout.close()
- out.close()
- in.close()
+ try scala.Console.withOut(bufout)(session())
+ finally {
+ bufout.close()
+ out.close()
+ in.close()
+ }
}
def run() {
@@ -86,12 +87,12 @@ abstract class SocketServer {
}
try {
- while (!shutDown) {
+ while (!shutdown) {
val clientSocket = try serverSocket.accept() catch {
case e: IOException => fail("Accept on port %d failed; exiting.")
}
- doSession(clientSocket)
- clientSocket.close()
+ try doSession(clientSocket)
+ finally clientSocket.close()
}
}
catch {