diff options
Diffstat (limited to 'src/compiler/scala')
6 files changed, 115 insertions, 50 deletions
diff --git a/src/compiler/scala/tools/nsc/CompileClient.scala b/src/compiler/scala/tools/nsc/CompileClient.scala index 9987eb0204..fd4f777890 100644 --- a/src/compiler/scala/tools/nsc/CompileClient.scala +++ b/src/compiler/scala/tools/nsc/CompileClient.scala @@ -16,18 +16,21 @@ import sys.SystemProperties.preferIPv4Stack class StandardCompileClient extends HasCompileSocket with CompileOutputCommon { lazy val compileSocket: CompileSocket = CompileSocket - val versionMsg = "Fast " + Properties.versionMsg - var verbose = false + val versionMsg = "Fast " + Properties.versionMsg + var verbose = false def process(args: Array[String]): Int = { - val fscArgs = args.toList - val settings = new FscSettings - val command = new CompilerCommand(fscArgs, settings) - verbose = settings.verbose.value - val shutdown = settings.shutdown.value - val vmArgs = settings.jvmargs.unparse ++ settings.defines.unparse ++ ( - if (settings.preferIPv4.value) List("-D%s=true".format(preferIPv4Stack.key)) else Nil - ) + // Trying to get out in front of the log messages in case we're + // going from verbose to not verbose. + verbose = (args contains "-verbose") + + val settings = new FscSettings(Console.println) + val command = new OfflineCompilerCommand(args.toList, settings) + val shutdown = settings.shutdown.value + val extraVmArgs = if (settings.preferIPv4.value) List("-D%s=true".format(preferIPv4Stack.key)) else Nil + + val vmArgs = settings.jvmargs.unparse ++ settings.defines.unparse ++ extraVmArgs + val fscArgs = args.toList ++ command.extraFscArgs if (settings.version.value) { Console println versionMsg @@ -35,7 +38,8 @@ class StandardCompileClient extends HasCompileSocket with CompileOutputCommon { } info(versionMsg) - info(fscArgs.mkString("[Given arguments: ", " ", "]")) + info(args.mkString("[Given arguments: ", " ", "]")) + info(fscArgs.mkString("[Transformed arguments: ", " ", "]")) info(vmArgs.mkString("[VM arguments: ", " ", "]")) val socket = diff --git a/src/compiler/scala/tools/nsc/CompileServer.scala b/src/compiler/scala/tools/nsc/CompileServer.scala index a34be6226b..866975b414 100644 --- a/src/compiler/scala/tools/nsc/CompileServer.scala +++ b/src/compiler/scala/tools/nsc/CompileServer.scala @@ -21,7 +21,9 @@ import settings.FscSettings */ class StandardCompileServer extends SocketServer { lazy val compileSocket: CompileSocket = CompileSocket + private var compiler: Global = null + private def clearCompiler() = compiler = null var reporter: ConsoleReporter = _ var shutdown = false @@ -58,7 +60,7 @@ class StandardCompileServer extends SocketServer { (totalMemory - freeMemory).toDouble / maxMemory.toDouble > MaxCharge } - protected def newOfflineCompilerCommand(arguments: List[String], settings: Settings) = + protected def newOfflineCompilerCommand(arguments: List[String], settings: FscSettings): OfflineCompilerCommand = new OfflineCompilerCommand(arguments, settings) /** Problematically, Settings are only considered equal if every setting @@ -69,7 +71,7 @@ class StandardCompileServer extends SocketServer { * and which do not interestingly influence compilation products. */ def unequalSettings(s1: Settings, s2: Settings): Set[Settings#Setting] = { - val ignoreSettings = Set("-d", "-encoding", "-verbose") + val ignoreSettings = Set("-d", "-encoding", "-currentDir") def trim (s: Settings): Set[Settings#Setting] = ( s.userSetSettings.toSet[Settings#Setting] filterNot (ss => ignoreSettings exists (ss respondsTo _)) ) @@ -80,7 +82,6 @@ class StandardCompileServer extends SocketServer { } def session() { - printMemoryStats() val password = compileSocket getPassword port val guessedPassword = in.readLine() val input = in.readLine() @@ -92,29 +93,34 @@ class StandardCompileServer extends SocketServer { if (input == null || password != guessedPassword) return - val args = input.split("\0", -1).toList - val settings = new FscSettings(fscError) - val command = newOfflineCompilerCommand(args, settings) + val args = input.split("\0", -1).toList + val newSettings = new FscSettings(fscError) + this.verbose = newSettings.verbose.value + val command = newOfflineCompilerCommand(args, newSettings) + + info("Settings after normalizing paths: " + newSettings) + printMemoryStats() // Update the idle timeout if given - if (!settings.idleMins.isDefault) { - val mins = settings.idleMins.value + if (!newSettings.idleMins.isDefault) { + val mins = newSettings.idleMins.value if (mins == 0) echo("Disabling idle timeout on compile server.") else echo("Setting idle timeout to " + mins + " minutes.") this.idleMinutes = mins } - if (settings.shutdown.value) { + if (newSettings.shutdown.value) { shutdown = true return out.println("[Compile server exited]") } - if (settings.reset.value) { - compiler = null - return out.println("[Compile server was reset]") + if (newSettings.reset.value) { + clearCompiler() + out.println("[Compile server was reset]") + if (command.files.isEmpty) + return } - this.verbose = settings.verbose.value - reporter = new ConsoleReporter(command.settings, in, out) { + reporter = new ConsoleReporter(newSettings, in, out) { // disable prompts, so that compile server cannot block override def displayPrompt = () } @@ -124,7 +130,7 @@ class StandardCompileServer extends SocketServer { info("[Compiler version: " + Properties.versionString + ".]") return false } - val unequal = unequalSettings(command.settings, compiler.settings) + val unequal = unequalSettings(newSettings, compiler.settings) if (unequal.nonEmpty) { info("[Replacing compiler with new instance because settings are unequal.]") info("[Asymmetric settings: " + unequal.mkString(", ") + "]") @@ -133,24 +139,26 @@ class StandardCompileServer extends SocketServer { } if (command.shouldStopWithInfo) - reporter.info(null, command.getInfoMessage(newGlobal(command.settings, reporter)), true) + reporter.info(null, command.getInfoMessage(newGlobal(newSettings, reporter)), true) else if (command.files.isEmpty) reporter.info(null, command.usageMsg, true) else { if (isCompilerReusable) { - compiler.settings = command.settings + info("[Reusing existing Global instance.]") + compiler.settings = newSettings compiler.reporter = reporter } else { - compiler = newGlobal(command.settings, reporter) + compiler = newGlobal(newSettings, reporter) } val c = compiler try new c.Run() compile command.files catch { case ex @ FatalError(msg) => reporter.error(null, "fatal error: " + msg) - compiler = null + clearCompiler() case ex => + warn("Compile server encountered fatal condition: " + ex) shutdown = true throw ex } @@ -158,7 +166,7 @@ class StandardCompileServer extends SocketServer { reporter.printSummary() if (isMemoryFullEnough) { info("Nulling out compiler due to memory utilization.") - compiler = null + clearCompiler() } } } diff --git a/src/compiler/scala/tools/nsc/CompilerCommand.scala b/src/compiler/scala/tools/nsc/CompilerCommand.scala index 1c0fd8b9de..f2c217e290 100644 --- a/src/compiler/scala/tools/nsc/CompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/CompilerCommand.scala @@ -46,22 +46,30 @@ class CompilerCommand(arguments: List[String], val settings: Settings) { | """.stripMargin.trim + "\n\n" + val shortUsage = "Usage: %s <options> <source files>" format cmdName + def createUsagePreface(shouldExplain: Boolean) = + if (shouldExplain) shortUsage + "\n" + explainAdvanced else "" + /** Creates a help message for a subset of options based on cond */ - def createUsageMsg(label: String, shouldExplain: Boolean, cond: Setting => Boolean): String = { + def createUsageMsg(cond: Setting => Boolean): String = { def helpStr(s: Setting) = format(s.helpSyntax) + " " + s.helpDescription - - val usage = "Usage: %s <options> <source files>\n" format cmdName - val explain = if (shouldExplain) explainAdvanced else "" - val prefix = label + " options include:\n " - // Separating out any debugging options from others for easier reading val (debug, rest) = (settings.visibleSettings filter cond).toList sortBy (_.name) partition (_.isForDebug) - (rest map helpStr).mkString(usage + explain + prefix, "\n ", "\n") + ( + (rest map helpStr).mkString("", "\n ", "\n") + ( if (debug.isEmpty) "" else (debug map helpStr).mkString("\nAdditional debug settings:\n ", "\n ", "\n") ) } + def createUsageMsg(label: String, shouldExplain: Boolean, cond: Setting => Boolean): String = { + val prefix = List( + Some(shortUsage), + Some(explainAdvanced) filter (_ => shouldExplain), + Some(label + " options include:\n ") + ).flatten mkString "\n" + + prefix + createUsageMsg(cond) + } /** Messages explaining usage and options */ def usageMsg = createUsageMsg("where possible standard", false, _.isStandard) diff --git a/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala b/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala index 786bcb1eb5..ef974da965 100644 --- a/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala +++ b/src/compiler/scala/tools/nsc/OfflineCompilerCommand.scala @@ -5,10 +5,38 @@ package scala.tools.nsc +import settings.FscSettings +import io.Directory + /** A compiler command for the offline compiler. * * @author Martin Odersky and Lex Spoon */ -class OfflineCompilerCommand(arguments: List[String], settings: Settings) extends CompilerCommand(arguments, settings) { +class OfflineCompilerCommand(arguments: List[String], settings: FscSettings) extends CompilerCommand(arguments, settings) { + import settings.currentDir + def extraFscArgs = List(currentDir.name, currentDir.value) + + locally { + // if -current-dir is unset, we're on the client and need to obtain it. + if (currentDir.isDefault) { + // Prefer env variable PWD to system property user.dir because the former + // deals better with paths not rooted at / (filesystem mounts.) + val baseDirectory = System.getenv("PWD") match { + case null => Directory.Current getOrElse Directory("/") + case dir => Directory(dir) + } + currentDir.value = baseDirectory.path + } + else { + // Otherwise we're on the server and will use it to absolutize the paths. + settings.absolutize(currentDir.value) + } + } + override def cmdName = "fsc" + override def usageMsg = ( + createUsageMsg("where possible fsc", false, x => x.isStandard && settings.isFscSpecific(x.name)) + + "\nStandard scalac options also available:\n " + + createUsageMsg(x => x.isStandard && !settings.isFscSpecific(x.name)) + ) } diff --git a/src/compiler/scala/tools/nsc/settings/FscSettings.scala b/src/compiler/scala/tools/nsc/settings/FscSettings.scala index a219148b16..bb62750026 100644 --- a/src/compiler/scala/tools/nsc/settings/FscSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/FscSettings.scala @@ -8,28 +8,45 @@ package nsc package settings import util.ClassPath +import io.{ Directory, Path, AbstractFile } class FscSettings(error: String => Unit) extends Settings(error) { outer => - def this() = this(Console.println) + locally { + disable(prompt) + disable(resident) + } + val currentDir = StringSetting ("-current-dir", "path", "Base directory for resolving relative paths", "").internalOnly() 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 preferIPv4 = BooleanSetting("-ipv4", "Use IPv4 rather than IPv6 for the server socket") - val absClasspath = BooleanSetting("-absolute-cp", "Make classpath elements absolute paths before sending to server") . - withPostSetHook (_ => absolutizeClasspath()) + 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) - disable(prompt) - disable(resident) + // For improved help output, separating fsc options from the others. + def fscSpecific = Set[Settings#Setting]( + currentDir, reset, shutdown, server, preferIPv4, idleMins + ) + val isFscSpecific: String => Boolean = fscSpecific map (_.name) + + /** If a setting (other than a PathSetting) represents a path or paths. + * For use in absolutization. + */ + private def holdsPath = Set[Settings#Setting]( + d, dependencyfile, pluginsDir, Ygenjavap + ) - // Make the classpath absolute: for going from client to server. - private def absolutizeClasspath() { - userSetSettings collect { - case x: PathSetting => x.value = ClassPath.makeAbsolute(x.value) + /** All user set settings rewritten with absolute paths. */ + def absolutize(root: Path) { + def rewrite(p: String) = (root resolve Path(p)).normalize.path + userSetSettings foreach { + case p: OutputSetting => p.outputDirs setSingleOutput AbstractFile.getDirectory(rewrite(p.value)) + case p: PathSetting => p.value = ClassPath.map(p.value, rewrite) + case p: StringSetting => if (holdsPath(p)) p.value = rewrite(p.value) + case _ => () } } } diff --git a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala index b485d4725a..12b9ef7154 100644 --- a/src/compiler/scala/tools/nsc/settings/MutableSettings.scala +++ b/src/compiler/scala/tools/nsc/settings/MutableSettings.scala @@ -479,7 +479,7 @@ class MutableSettings(val errorFn: String => Unit) extends AbsSettings with Scal /** Set the output directory. */ class OutputSetting private[nsc]( - outputDirs: OutputDirs, + private[nsc] val outputDirs: OutputDirs, default: String) extends StringSetting("-d", "directory", "Specify where to place generated class files", default) { value = default |