From 388a0c0d1db3e41e3acbebd6e1e4c79f7d176ca3 Mon Sep 17 00:00:00 2001 From: Paul Phillips Date: Thu, 18 Feb 2010 18:49:21 +0000 Subject: The first reasonably satisfying classpath commit. there with this one. Documentation to come. Review by community. --- .../scala/tools/nsc/GenericRunnerSettings.scala | 2 - src/compiler/scala/tools/nsc/Interpreter.scala | 2 +- src/compiler/scala/tools/nsc/InterpreterLoop.scala | 4 +- .../scala/tools/nsc/MainGenericRunner.scala | 4 +- src/compiler/scala/tools/nsc/ScriptRunner.scala | 4 +- src/compiler/scala/tools/nsc/Settings.scala | 60 ++--- .../scala/tools/nsc/backend/JavaPlatform.scala | 12 +- .../scala/tools/nsc/interpreter/Completion.scala | 2 +- src/compiler/scala/tools/nsc/util/ClassPath.scala | 11 + .../scala/tools/util/ClassPathSettings.scala | 37 +++ src/compiler/scala/tools/util/PathResolver.scala | 251 +++++---------------- src/compiler/scala/tools/util/StringOps.scala | 66 +++++- .../scala/tools/partest/nest/CompileManager.scala | 4 +- .../tools/partest/nest/ConsoleFileManager.scala | 62 ++--- .../scala/tools/partest/nest/ConsoleRunner.scala | 4 +- .../scala/tools/partest/nest/TestFile.scala | 6 +- src/partest/scala/tools/partest/package.scala | 14 +- src/scalap/scala/tools/scalap/Main.scala | 2 +- test/files/run/programmatic-main.scala | 5 +- 19 files changed, 237 insertions(+), 315 deletions(-) create mode 100644 src/compiler/scala/tools/util/ClassPathSettings.scala diff --git a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala index 76cb8e608b..6697146a5a 100644 --- a/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala +++ b/src/compiler/scala/tools/nsc/GenericRunnerSettings.scala @@ -38,6 +38,4 @@ extends Settings(error) { BooleanSetting( "-nocompdaemon", "do not use the fsc compilation daemon") - - val defines = DefinesSetting } diff --git a/src/compiler/scala/tools/nsc/Interpreter.scala b/src/compiler/scala/tools/nsc/Interpreter.scala index 3b6b7a1a7f..6e06518a7a 100644 --- a/src/compiler/scala/tools/nsc/Interpreter.scala +++ b/src/compiler/scala/tools/nsc/Interpreter.scala @@ -192,7 +192,7 @@ class Interpreter(val settings: Settings, out: PrintWriter) { /** the compiler's classpath, as URL's */ /** XXX ignoring codebase for now, but it used to be appended here. */ /** (And one would think it ought to be prepended. */ - lazy val compilerClasspath: List[URL] = new PathResolver(settings) minimalPathAsURLs + lazy val compilerClasspath: List[URL] = new PathResolver(settings) asURLs /* A single class loader is used for all commands interpreted by this Interpreter. It would also be possible to create a new class loader for each command diff --git a/src/compiler/scala/tools/nsc/InterpreterLoop.scala b/src/compiler/scala/tools/nsc/InterpreterLoop.scala index 7060c14ea2..c04feb9efd 100644 --- a/src/compiler/scala/tools/nsc/InterpreterLoop.scala +++ b/src/compiler/scala/tools/nsc/InterpreterLoop.scala @@ -112,8 +112,8 @@ class InterpreterLoop(in0: Option[BufferedReader], out: PrintWriter) { /** Create a new interpreter. */ def createInterpreter() { - if (!addedClasspath.isEmpty) - addedClasspath foreach (settings appendToClasspath _) + // if (!addedClasspath.isEmpty) + // addedClasspath foreach (settings appendToClasspath _) interpreter = new Interpreter(settings, out) { override protected def parentClassLoader = classOf[InterpreterLoop].getClassLoader diff --git a/src/compiler/scala/tools/nsc/MainGenericRunner.scala b/src/compiler/scala/tools/nsc/MainGenericRunner.scala index 719425c40c..bb373c69e4 100644 --- a/src/compiler/scala/tools/nsc/MainGenericRunner.scala +++ b/src/compiler/scala/tools/nsc/MainGenericRunner.scala @@ -13,7 +13,7 @@ import java.lang.reflect.InvocationTargetException import java.net.{ URL, MalformedURLException } import scala.tools.util.PathResolver -import io.{ File } +import io.{ File, Process } import util.{ ClassPath, ScalaClassLoader } import Properties.{ versionString, copyrightString } @@ -43,7 +43,7 @@ object MainGenericRunner { def dashi = settings.loadfiles.value def slurp = dashi map (file => File(file).slurp()) mkString "\n" - val classpath: List[URL] = PathResolver urlsFromSettings settings + val classpath: List[URL] = new PathResolver(settings) asURLs /** Was code given in a -e argument? */ if (!settings.execute.isDefault) { diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala index 5d5e4c8c43..0cd5fe1f6a 100644 --- a/src/compiler/scala/tools/nsc/ScriptRunner.scala +++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala @@ -300,8 +300,8 @@ object ScriptRunner compiledLocation: String, scriptArgs: List[String]): Boolean = { - val classpath = - (PathResolver urlsFromSettings settings) ::: (PathResolver fromPathString compiledLocation asURLs) + val pr = new PathResolver(settings) + val classpath = pr.asURLs :+ new URL(compiledLocation) try { ObjectRunner.run( diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala index 9585db7c64..39ea8f62c6 100644 --- a/src/compiler/scala/tools/nsc/Settings.scala +++ b/src/compiler/scala/tools/nsc/Settings.scala @@ -12,20 +12,13 @@ import io.AbstractFile import util.{ ClassPath, SourceFile } import Settings._ import annotation.elidable -import scala.tools.util.PathResolver +import scala.tools.util.{ PathResolver, StringOps } import scala.collection.mutable.ListBuffer import scala.collection.immutable.TreeSet class Settings(errorFn: String => Unit) extends ScalacSettings { def this() = this(Console.println) - /** It's a hacky situation but there's not much to be done in the - * face of settings which mutate and semantic significance to the - * originally given classpath. - */ - private var _userSuppliedClassPath: String = null - def userSuppliedClassPath = if (_userSuppliedClassPath == null) "" else _userSuppliedClassPath - /** Iterates over the arguments applying them to settings where applicable. * Then verifies setting dependencies are met. * @@ -47,23 +40,13 @@ class Settings(errorFn: String => Unit) extends ScalacSettings { var args = arguments val residualArgs = new ListBuffer[String] - /** First time through here we take note of the classpath, if any. - */ - def finish[T](x: T): T = { - /** "lazy var" */ - if (_userSuppliedClassPath == null) - _userSuppliedClassPath = if (classpath.isDefault) "" else classpath.value - - x - } - while (args.nonEmpty) { if (args.head startsWith "-") { val args0 = args args = this parseParams args if (args eq args0) { errorFn("bad option: '" + args.head + "'") - return finish((false, args)) + return ((false, args)) } } else if (args.head == "") { // discard empties, sometimes they appear because of ant or etc. @@ -71,14 +54,14 @@ class Settings(errorFn: String => Unit) extends ScalacSettings { } else { if (!processAll) - return finish((checkDependencies, args)) + return ((checkDependencies, args)) residualArgs += args.head args = args.tail } } - finish((checkDependencies, residualArgs.toList)) + ((checkDependencies, residualArgs.toList)) } def processArgumentString(params: String) = processArguments(splitParams(params), true) @@ -124,7 +107,7 @@ class Settings(errorFn: String => Unit) extends ScalacSettings { /** Split the given line into parameters. */ - def splitParams(line: String) = PathResolver.splitParams(line, errorFn) + def splitParams(line: String) = StringOps.splitParams(line, errorFn) /** Returns any unprocessed arguments. */ @@ -145,19 +128,18 @@ class Settings(errorFn: String => Unit) extends ScalacSettings { // if arg is of form -Xfoo:bar,baz,quux def parseColonArg(s: String): Option[List[String]] = { - val idx = s indexWhere (_ == ':') - val (p, args) = (s.substring(0, idx), s.substring(idx+1).split(",").toList) + val (p, args) = StringOps.splitWhere(s, _ == ':', true) getOrElse (return None) // any non-Nil return value means failure and we return s unmodified - tryToSetIfExists(p, args, (s: Setting) => s.tryToSetColon _) + tryToSetIfExists(p, args split "," toList, (s: Setting) => s.tryToSetColon _) } // if arg is of form -Dfoo=bar or -Dfoo (name = "-D") - def isPropertyArg(s: String) = lookupSetting(s.substring(0, 2)) match { + def isPropertyArg(s: String) = lookupSetting(s take 2) match { case Some(x: DefinesSetting) => true case _ => false } def parsePropertyArg(s: String): Option[List[String]] = { - val (p, args) = (s.substring(0, 2), s.substring(2)) + val (p, args) = (s take 2, s drop 2) tryToSetIfExists(p, List(args), (s: Setting) => s.tryToSetProperty _) } @@ -258,8 +240,8 @@ class Settings(errorFn: String => Unit) extends ScalacSettings { lazy val ChoiceSetting = untupled((choice _).tupled andThen add[ChoiceSetting]) lazy val DebugSetting = untupled((sdebug _).tupled andThen add[DebugSetting]) lazy val PhasesSetting = untupled((phase _).tupled andThen add[PhasesSetting]) - lazy val DefinesSetting = add(defines()) lazy val OutputSetting = untupled((output _).tupled andThen add[OutputSetting]) + lazy val DefinesSetting = () => add(new DefinesSetting()) override def toString() = "Settings(\n%s)" format (userSetSettings map (" " + _ + "\n") mkString) @@ -408,8 +390,6 @@ object Settings { def phase(name: String, descr: String) = new PhasesSetting(name, descr) - def defines() = new DefinesSetting() - def output(outputDirs: OutputDirs, default: String) = new OutputSetting(outputDirs, default) } @@ -653,7 +633,7 @@ object Settings { extends Setting(descr + choices.mkString(" (", ",", ")")) { type T = String protected var v: String = default - protected def argument: String = name.substring(1) + protected def argument: String = name drop 1 def tryToSet(args: List[String]) = { value = default ; Some(args) } override def tryToSetColon(args: List[String]) = args match { @@ -747,12 +727,10 @@ object Settings { // given foo=bar returns Some(foo, bar), or None if parse fails def parseArg(s: String): Option[(String, String)] = { if (s == "") return None - val regexp = """^(.*)?=(.*)$""".r + val idx = s indexOf '=' - regexp.findAllIn(s).matchData.toList match { - case Nil => Some(s, "") - case List(md) => md.subgroups match { case List(a,b) => Some(a,b) } - } + if (idx < 0) Some(s, "") + else Some(s take idx, s drop (idx + 1)) } private[Settings] override def tryToSetProperty(args: List[String]): Option[List[String]] = @@ -815,12 +793,14 @@ trait ScalacSettings { */ val bootclasspath = StringSetting ("-bootclasspath", "path", "Override location of bootstrap class files", "") - val classpath = StringSetting ("-classpath", "path", "Specify where to find user class files", "") . - withAbbreviation("-cp") . - withPostSetHook(self => if (Ylogcp.value) Console.println("Updated classpath to '%s'".format(self.value))) + val classpath = StringSetting ("-classpath", "path", "Specify where to find user class files", "") withAbbreviation ("-cp") val extdirs = StringSetting ("-extdirs", "dirs", "Override location of installed extensions", "") val javabootclasspath = StringSetting ("-javabootclasspath", "path", "Override java boot classpath.", "") + val javabootAppend = StringSetting ("-javabootclasspath/a", "path", "Append to java boot classpath", "") + val javabootPrepend = StringSetting ("-javabootclasspath/p", "path", "Prepend to java boot classpath", "") val javaextdirs = StringSetting ("-javaextdirs", "path", "Override java extdirs classpath.", "") + + val outdir = OutputSetting (outputDirs, ".") val sourcepath = StringSetting ("-sourcepath", "path", "Specify where to find input source files", "") val Ycodebase = StringSetting ("-Ycodebase", "codebase", "Specify the URL containing the Scala libraries", "") val Ylogcp = BooleanSetting ("-Ylog-classpath", "Output information about what classpath is being applied.") @@ -830,7 +810,7 @@ trait ScalacSettings { */ // argfiles is only for the help message val argfiles = BooleanSetting ("@", "A text file containing compiler arguments (options and source files)") - val outdir = OutputSetting (outputDirs, ".") + val defines = DefinesSetting() val dependenciesFile = StringSetting ("-dependencyfile", "file", "Specify the file in which dependencies are tracked", ".scala_dependencies") val deprecation = BooleanSetting ("-deprecation", "Output source locations where deprecated APIs are used") val encoding = StringSetting ("-encoding", "encoding", "Specify character encoding used by source files", Properties.sourceEncoding) diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala index b6dd06f83f..88651220f6 100644 --- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala @@ -14,17 +14,7 @@ import scala.tools.util.PathResolver trait JavaPlatform extends Platform[AbstractFile] { import global._ - lazy val classPath: JavaClassPath = { - val context = - if (isInlinerOn) new JavaContext - else DefaultJavaContext - - val cp = PathResolver.fromSettings(settings, context) - if (settings.Ylogcp.value) - Console.println("Created Global has classpath: " + cp.asClasspathString) - - cp - } + lazy val classPath = new PathResolver(settings).result def rootLoader = new loaders.JavaPackageLoader(classPath) diff --git a/src/compiler/scala/tools/nsc/interpreter/Completion.scala b/src/compiler/scala/tools/nsc/interpreter/Completion.scala index 65a6da7c07..b62de995c2 100644 --- a/src/compiler/scala/tools/nsc/interpreter/Completion.scala +++ b/src/compiler/scala/tools/nsc/interpreter/Completion.scala @@ -53,7 +53,7 @@ import Completion._ class Completion(repl: Interpreter) { self => - private lazy val classPath = PathResolver.fromSettings(repl.settings).asURLs + private lazy val classPath = repl.compilerClasspath // the unqualified vals/defs/etc visible in the repl val ids = new IdentCompletion(repl) diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index 013cd89a44..0e9c4f27fa 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -213,6 +213,13 @@ abstract class ClassPath[T] { case Some(ClassRep(Some(x: AbstractFile), _)) => Some(x) case _ => None } + + def sortString = asURLs map (_.toString) sorted + override def equals(that: Any) = that match { + case x: ClassPath[_] => this.sortString == x.sortString + case _ => false + } + override def hashCode = sortString.hashCode } /** @@ -311,6 +318,10 @@ extends ClassPath[T] { case x: DirectoryClassPath => x.dir.path case x: MergedClassPath[_] => x.asClasspathString }) + def show { + println("ClassPath %s has %d entries and results in:\n".format(name, entries.size)) + asClasspathString split ':' foreach (x => println(" " + x)) + } override def toString() = "merged classpath "+ entries.mkString("(", "\n", ")") } diff --git a/src/compiler/scala/tools/util/ClassPathSettings.scala b/src/compiler/scala/tools/util/ClassPathSettings.scala new file mode 100644 index 0000000000..49c01d19df --- /dev/null +++ b/src/compiler/scala/tools/util/ClassPathSettings.scala @@ -0,0 +1,37 @@ +/* NSC -- new Scala compiler + * Copyright 2006-2010 LAMP/EPFL + * @author Paul Phillips + */ + +package scala.tools +package util + +trait ClassPathSettings { + def javabootclasspath: String // -javabootclasspath + def javabootPrepend: String // -javabootclasspath/p + def javabootAppend: String // -javabootclasspath/a + def javaextdirs: String // -javaextdirs + + def bootclasspath: String // -bootclasspath + def extdirs: String // -extdirs + def classpath: String // -classpath + def sourcepath: String // -sourcepath + + def codebase: String // -Ycodebase +} + +// val debugLogger = { +// val f = File("/tmp/path-resolve-log.txt") +// if (f.exists) f.truncate() +// else f.createFile() +// +// val res = f.bufferedWriter() +// res write ("Started debug log: %s\n".format(new java.util.Date)) +// res +// } +// def log(msg: Any) = { +// Console println msg +// debugLogger.write(msg.toString + "\n") +// debugLogger flush +// } + diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala index 9feb61e0a3..c86c6ffe74 100644 --- a/src/compiler/scala/tools/util/PathResolver.scala +++ b/src/compiler/scala/tools/util/PathResolver.scala @@ -13,29 +13,13 @@ import nsc.io.{ File, Directory, Path } import ClassPath.{ JavaContext, DefaultJavaContext, join, split } import PartialFunction.condOpt -// Mostly based on the specification at: +// Loosely based on the draft specification at: // https://lampsvn.epfl.ch/trac/scala/wiki/Classpath -// object PathResolver { - // val debugLogger = { - // val f = File("/tmp/path-resolve-log.txt") - // if (f.exists) f.truncate() - // else f.createFile() - // - // val res = f.bufferedWriter() - // res write ("Started debug log: %s\n".format(new java.util.Date)) - // res - // } - // def log(msg: Any) = { - // Console println msg - // debugLogger.write(msg.toString + "\n") - // debugLogger flush - // } - - private def propOrElse(name: String, alt: String) = System.getProperty(name, alt) - private def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt - private def firstNonEmpty(xs: String*) = xs find (_ != "") getOrElse "" + def propOrElse(name: String, alt: String) = System.getProperty(name, alt) + def envOrElse(name: String, alt: String) = Option(System getenv name) getOrElse alt + def firstNonEmpty(xs: String*) = xs find (_ != "") getOrElse "" private def fileOpt(f: Path): Option[String] = f ifFile (_.path) private def dirOpt(d: Path): Option[String] = d ifDirectory (_.path) @@ -60,15 +44,21 @@ object PathResolver { import scala.collection.JavaConversions._ System.getProperties find (_._1 endsWith ".boot.class.path") map (_._2) getOrElse "" } + private def searchForScalaHome = { + for (url <- ScalaClassLoader originOfClass classOf[ScalaObject] ; if url.getProtocol == "file") yield + File(url.getFile).parent.path + } getOrElse "" def classPathEnv = envOrElse("CLASSPATH", "") def sourcePathEnv = envOrElse("SOURCEPATH", "") // not used + def scalaHomeEnv = envOrElse("SCALA_HOME", "") // not used def javaBootClassPath = propOrElse("sun.boot.class.path", searchForBootClasspath) def javaUserClassPath = propOrElse("java.class.path", "") def javaExtDirs = propOrElse("java.ext.dirs", "") def userHome = propOrElse("user.home", "") def scalaHome = propOrElse("scala.home", "") def scalaExtDirs = propOrElse("scala.ext.dirs", "") + def scalaHomeGuessed = searchForScalaHome override def toString = """ |object Environment { @@ -88,17 +78,13 @@ object PathResolver { * to the path resolution specification. */ object Defaults { - private lazy val guessedScalaHome = { - for (url <- ScalaClassLoader originOfClass classOf[ScalaObject] ; if url.getProtocol == "file") yield - File(url.getFile).parent.path - } getOrElse "" - - // XXX review these semantics - def javaBootClassPath = join(Seq(Environment.javaBootClassPath, Environment.javaUserClassPath)) // ... ignoring Environment.classPathEnv + def javaBootClassPath = Environment.javaBootClassPath + def javaUserClassPath = firstNonEmpty(Environment.javaUserClassPath, Environment.classPathEnv) def javaExtDirs = Environment.javaExtDirs - def scalaHome = firstNonEmpty(Environment.scalaHome, guessedScalaHome) + def scalaHome = Environment.scalaHome def scalaHomeDir = Directory(scalaHome) + def scalaHomeExists = scalaHomeDir.isDirectory def scalaLibDir = Directory(scalaHomeDir / "lib") def scalaClassesDir = Directory(scalaHomeDir / "classes") @@ -115,31 +101,15 @@ object PathResolver { else if (scalaLibAsDir.isDirectory) scalaLibAsDir.path else "" - // This attempt to duplicate the original logic of MainGenericRunner - // was causing the issue described in r20878. Obviously it's past time - // to establish with certainty what each of these paths should contain. - def scalaBootClassPath = "" - // scalaLibDirFound match { - // case Some(dir) => join(ClassPath expandDir dir.path) - // case _ => "" - // } + def scalaBootClassPath = scalaLibDirFound match { + case Some(dir) if scalaHomeExists => join(ClassPath expandDir dir.path) + case _ => "" + } def scalaExtDirs = Environment.scalaExtDirs def scalaPluginDirs = List("misc", "scala-devel", "plugins") def scalaPluginPath = join(scalaPluginDirs map (scalaHomeDir / _ path)) - // The class path that a runner script uses to interpret a program is called the “execution class path”. - // The execution class path is the concatenation of the following sub-path. - // If a class is available in multiple locations, it must be loaded from that with the lowest number. - def executionPath = List( - // 1. The Java bootstrap class path. - javaBootClassPath, - // 2. The Java extension class path. - javaExtDirs, - // 3. The class path formed by all JAR and ZIP files and all folders in Scala's home lib folder. - scalaBootClassPath - ) - override def toString = """ |object Defaults { | javaBootClassPath = %s @@ -155,60 +125,10 @@ object PathResolver { ) } - def executionPath = join(Defaults.executionPath) - def executionPathURLs = fromPathString(executionPath).asURLs - - private def classPathContainersFromSettings(settings: Settings, context: JavaContext) = { - val pr = new PathResolver(settings) - import context._ - import pr.Calculated._ - - // XXX how should the contents of lib/* break down between bootclasspath and extdirs? - // XXX what exactly is codebase for? - val sources = List( - classesInPath(javaBootClassPath), // -javabootclasspath multiple entries, no expansion - contentsOfDirsInPath(scalaBootClassPath), // -bootclasspath ??? - contentsOfDirsInPath(javaExtDirs), // -javaextdirs multiple dirs, each expands to contents - contentsOfDirsInPath(scalaExtDirs), // -extdirs ??? - classesInExpandedPath(userClassPath), // -classpath multiple entries, first expanding *s - classesAtAllURLS(codeBase), // -Ycodebase ??? multiple URLs - sourcesInPath(sourcePath) // -sourcepath multiple source entries, no expansion - ) - - if (settings.Ylogcp.value) - Console.println("PathResolver calculated classpath:\n" + pr.Calculated) - - sources.flatten - } - def urlsFromSettings(settings: Settings): List[URL] = urlsFromSettings(settings, DefaultJavaContext) - def urlsFromSettings(settings: Settings, context: JavaContext): List[URL] = - classPathContainersFromSettings(settings, context) flatMap (_.asURLs) distinct - - private def contextFromSettings(s: Settings) = - if (s.inline.value) new JavaContext else DefaultJavaContext - - def fromArgumentString(argString: String): JavaClassPath = - fromArgumentList(splitParams(argString, _ => ())) - - def fromArgumentList(args: List[String]): JavaClassPath = { - val settings = new Settings() - settings.processArguments(args, false) - fromSettings(settings, contextFromSettings(settings)) - } - - def fromSettings(settings: Settings): JavaClassPath = - fromSettings(settings, contextFromSettings(settings)) - - def fromSettings(settings: Settings, context: JavaContext): JavaClassPath = { - val containers = classPathContainersFromSettings(settings, context) - new JavaClassPath(containers, context) - } - - def fromPathString(path: String): JavaClassPath = fromPathString(path, DefaultJavaContext) - def fromPathString(path: String, context: JavaContext): JavaClassPath = { + def fromPathString(path: String, context: JavaContext = DefaultJavaContext): JavaClassPath = { val s = new Settings() s.classpath.value = path - fromSettings(s, context) + new PathResolver(s, context) result } /** With no arguments, show the interesting values in Environment and Defaults. @@ -226,76 +146,15 @@ object PathResolver { val pr = new PathResolver(settings) println(" COMMAND: 'scala %s'".format(args.mkString(" "))) println("RESIDUAL: 'scala %s'\n".format(rest.mkString(" "))) - println(pr.Calculated) + pr.result.show } } - - /** - * Split command line parameters by space, properly process quoted parameter - */ - def splitParams(line: String, errorFn: String => Unit): List[String] = { - def parse(from: Int, i: Int, args: List[String]): List[String] = { - if (i < line.length) { - line.charAt(i) match { - case ' ' => - val args1 = fetchArg(from, i) :: args - val j = skipS(i + 1) - if (j >= 0) { - parse(j, j, args1) - } else args1 - case '"' => - val j = skipTillQuote(i + 1) - if (j > 0) { - parse(from, j + 1, args) - } else { - errorFn("Parameters '" + line + "' with unmatched quote at " + i + ".") - Nil - } - case _ => parse(from, i + 1, args) - } - } else { // done - if (i > from) { - fetchArg(from, i) :: args - } else args - } - } - - def fetchArg(from: Int, until: Int) = { - if (line.charAt(from) == '"') { - line.substring(from + 1, until - 1) - } else { - line.substring(from, until) - } - } - - def skipTillQuote(i: Int): Int = { - if (i < line.length) { - line.charAt(i) match { - case '"' => i - case _ => skipTillQuote(i + 1) - } - } else -1 - } - - def skipS(i: Int): Int = { - if (i < line.length) { - line.charAt(i) match { - case ' ' => skipS(i + 1) - case _ => i - } - } else -1 - } - - // begin split - val j = skipS(0) - if (j >= 0) { - parse(j, j, Nil).reverse - } else Nil - } } import PathResolver.{ Defaults, Environment, firstNonEmpty, ppcp } -class PathResolver(settings: Settings) { +class PathResolver(settings: Settings, context: JavaContext) { + def this(settings: Settings) = this(settings, if (settings.inline.value) new JavaContext else DefaultJavaContext) + private def cmdLineOrElse(name: String, alt: String) = { (commandLineFor(name) match { case Some("") => None @@ -319,56 +178,58 @@ class PathResolver(settings: Settings) { object Calculated { def scalaHome = Defaults.scalaHome def javaBootClassPath = cmdLineOrElse("javabootclasspath", Defaults.javaBootClassPath) - def scalaBootClassPath = cmdLineOrElse("bootclasspath", Defaults.scalaBootClassPath) def javaExtDirs = cmdLineOrElse("javaextdirs", Defaults.javaExtDirs) + def javaUserClassPath = Defaults.javaUserClassPath + def scalaBootClassPath = cmdLineOrElse("bootclasspath", Defaults.scalaBootClassPath) def scalaExtDirs = cmdLineOrElse("extdirs", Defaults.scalaExtDirs) - def userClassPath = cmdLineOrElse("classpath", "") + def userClassPath = cmdLineOrElse("classpath", ".") def sourcePath = cmdLineOrElse("sourcepath", "") def codeBase = cmdLineOrElse("Ycodebase", "") - def dotPath = if (settings.userSuppliedClassPath == "") "." else "" - def referencePath = List( - // 1. The value of -javabootclasspath if it is set, or the Java bootstrap class path. - javaBootClassPath, - // 2. The value of -bootclasspath if it is set, - // or the lib/scala-library.jar file of Scala's home if it is available, - // or the classes/library folder of Scala's home if it is available. - scalaBootClassPath, - // 3. All JAR and ZIP files present in any folder listed by the value of -javaextdirs, if it is set, - // or the Java extension class path. - javaExtDirs, - // 4. All JAR and ZIP files present in any folder listed by the value of -extdirs, if it is set. - scalaExtDirs, - // 5. The first available path below. - // * The value of -classpath or -cp. - // * ---> XXX what about java.class.path? - // * The value of the CLASSPATH environment variable. - // * The current directory (that is the location of "."). - userClassPath, - dotPath + import context._ + + // Assemble the elements! + def basis = List( + classesInPath(javaBootClassPath), // 1. The Java bootstrap class path. + contentsOfDirsInPath(javaExtDirs), // 2. The Java extension class path. + classesInExpandedPath(javaUserClassPath), // 3. The Java application class path. + classesInPath(scalaBootClassPath), // 4. The Scala boot class path. + contentsOfDirsInPath(scalaExtDirs), // 5. The Scala extension class path. + classesInExpandedPath(userClassPath), // 6. The Scala application class path. + sourcesInPath(sourcePath) // 7. The Scala source path. ) + lazy val containers = basis.flatten.distinct + override def toString = """ |object Calculated { | scalaHome = %s | javaBootClassPath = %s + | javaUserClassPath = %s | scalaBootClassPath = %s | javaExtDirs = %s | scalaExtDirs = %s | userClassPath = %s | sourcePath = %s - | referencePath = %s |}""".trim.stripMargin.format( scalaHome, - ppcp(javaBootClassPath), ppcp(scalaBootClassPath), + ppcp(javaBootClassPath), ppcp(javaUserClassPath), ppcp(scalaBootClassPath), ppcp(javaExtDirs), ppcp(scalaExtDirs), - ppcp(userClassPath), ppcp(sourcePath), - ppcp(PathResolver.this.referencePath) + ppcp(userClassPath), ppcp(sourcePath) ) } - def referencePath = join(Calculated.referencePath) - def referencePathAsURLs = ClassPath toURLs referencePath - def minimalPath = join(Seq(Calculated.scalaBootClassPath, Calculated.userClassPath)) - def minimalPathAsURLs = ClassPath toURLs minimalPath + def containers = Calculated.containers + + lazy val result = { + val cp = new JavaClassPath(containers, context) + if (settings.Ylogcp.value) { + Console.println("Classpath built from settings: " + settings) + Console.println("And Environment: " + PathResolver.Environment) + cp.show + } + cp + } + + def asURLs = result.asURLs } diff --git a/src/compiler/scala/tools/util/StringOps.scala b/src/compiler/scala/tools/util/StringOps.scala index 77fc6f8700..b98ab38d89 100644 --- a/src/compiler/scala/tools/util/StringOps.scala +++ b/src/compiler/scala/tools/util/StringOps.scala @@ -11,13 +11,75 @@ package scala.tools package util -/** This objects provides methods to extract elements from - * a string according to some defined character separator. +/** This object provides utility methods to extract elements + * from Strings. * * @author Martin Odersky * @version 1.0 */ object StringOps { + /** + * Split command line parameters by space, properly process quoted parameter + */ + def splitParams(line: String, errorFn: String => Unit): List[String] = { + def parse(from: Int, i: Int, args: List[String]): List[String] = { + if (i < line.length) { + line.charAt(i) match { + case ' ' => + val args1 = fetchArg(from, i) :: args + val j = skipS(i + 1) + if (j >= 0) { + parse(j, j, args1) + } else args1 + case '"' => + val j = skipTillQuote(i + 1) + if (j > 0) { + parse(from, j + 1, args) + } else { + errorFn("Parameters '" + line + "' with unmatched quote at " + i + ".") + Nil + } + case _ => parse(from, i + 1, args) + } + } else { // done + if (i > from) { + fetchArg(from, i) :: args + } else args + } + } + + def fetchArg(from: Int, until: Int) = { + if (line.charAt(from) == '"') { + line.substring(from + 1, until - 1) + } else { + line.substring(from, until) + } + } + + def skipTillQuote(i: Int): Int = { + if (i < line.length) { + line.charAt(i) match { + case '"' => i + case _ => skipTillQuote(i + 1) + } + } else -1 + } + + def skipS(i: Int): Int = { + if (i < line.length) { + line.charAt(i) match { + case ' ' => skipS(i + 1) + case _ => i + } + } else -1 + } + + // begin split + val j = skipS(0) + if (j >= 0) { + parse(j, j, Nil).reverse + } else Nil + } def decompose(str: String, sep: Char): List[String] = { def ws(start: Int): List[String] = diff --git a/src/partest/scala/tools/partest/nest/CompileManager.scala b/src/partest/scala/tools/partest/nest/CompileManager.scala index 3ef5a0f723..63acf654e2 100644 --- a/src/partest/scala/tools/partest/nest/CompileManager.scala +++ b/src/partest/scala/tools/partest/nest/CompileManager.scala @@ -45,7 +45,7 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { settings.deprecation.value = true settings.nowarnings.value = false settings.encoding.value = "iso-8859-1" - settings.classpath.value += fileManager.LATEST_LIB + settings.classpath.value += (pathSeparator + fileManager.LATEST_LIB) // XXX // settings.javabootAppend.value = fileManager.LATEST_LIB @@ -113,7 +113,7 @@ class DirectCompiler(val fileManager: FileManager) extends SimpleCompiler { out map { outDir => command.settings.outdir.value = outDir.getAbsolutePath - command.settings appendToClasspath outDir.getAbsolutePath + command.settings.classpath.value += (pathSeparator + outDir.getAbsolutePath) } val toCompile = files map (_.getPath) diff --git a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala index cd5023573b..258651e0ce 100644 --- a/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala +++ b/src/partest/scala/tools/partest/nest/ConsoleFileManager.scala @@ -14,6 +14,7 @@ import scala.tools.util.PathResolver import scala.tools.nsc.io import io.{ Path, Directory } import File.pathSeparator +import PathResolver.{ propOrElse } class ConsoleFileManager extends FileManager { implicit private def tempPathConversion(x: Path): File = x.jfile @@ -47,17 +48,11 @@ class ConsoleFileManager extends FileManager { NestUI.verbose("CLASSPATH: "+CLASSPATH) - var JAVACMD = System.getProperty("scalatest.javacmd", "java") - var JAVAC_CMD = System.getProperty("scalatest.javac_cmd", "javac") + var JAVACMD = propOrElse("scalatest.javacmd", "java") + var JAVAC_CMD = propOrElse("scalatest.javac_cmd", "javac") - val prefixFile = { - val cwd = System.getProperty("user.dir") - if (cwd != null) - (new File(cwd)).getCanonicalFile - else - error("user.dir property not set") - } - val PREFIX = prefixFile.getAbsolutePath + val prefixDir = Directory.Current map (_.normalize.toDirectory) getOrElse error("user.dir property not set") + val PREFIX = prefixDir.toAbsolute.path /* if [ -d "$PREFIX/test" ]; then @@ -68,38 +63,31 @@ else abort "Test directory not found"; */ - val testRootFile = { - val testRootProp = System.getProperty("scalatest.root") - val testroot = - if (testRootProp != null) - new File(testRootProp) - else { - // case 1: cwd is `test` - if (prefixFile.getName == "test" && (new File(prefixFile, "files")).exists) - prefixFile - else { - // case 2: cwd is `test/..` - val test = new File(prefixFile, "test") - val scalaTest = new File(new File(prefixFile, "misc"), "scala-test") - if (test.isDirectory) - test - else if (scalaTest.isDirectory) - scalaTest - else - error("Test directory not found") - } - } - testroot.getCanonicalFile + val testRootDir = { + val testRootProp = Option(propOrElse("scalatest.root", null)) map (x => Directory(x)) + def isTestDir(d: Directory) = d.name == "test" && (d / "files" isDirectory) + + ( + testRootProp orElse ( + if (isTestDir(prefixDir)) Some(prefixDir) else None // cwd is `test` + ) orElse ( + (prefixDir / "test") ifDirectory (x => x) // cwd is `test/..` + ) orElse ( + (prefixDir / "misc" / "scala-test") ifDirectory (x => x) + ) getOrElse ( + error("Test directory not found") + ) + ).normalize } - val TESTROOT = testRootFile.getAbsolutePath + val TESTROOT = testRootDir.toAbsolute.path - def testParent = Path(testRootFile).parent + def testParent = testRootDir.parent var srcDirName: String = "" val srcDir: io.Directory = { srcDirName = Option(System.getProperty("partest.srcdir")) getOrElse "files" - val src = Path(testRootFile) / srcDirName + val src = testRootDir / srcDirName if (src.isDirectory) src.toDirectory else { @@ -108,7 +96,7 @@ else } } - LIB_DIR = (Path(testRootFile.getParentFile) / "lib").normalize.toAbsolute.path + LIB_DIR = (testParent / "lib").normalize.toAbsolute.path CLASSPATH = { val libs = (srcDir / Directory("lib")).files filter (_ hasExtension "jar") map (_.normalize.toAbsolute.path) @@ -177,7 +165,7 @@ else val dists = testParent / "dists" val build = testParent / "build" - // in case of an installed dist, testRootFile is one level deeper + // in case of an installed dist, testRootDir is one level deeper val bin = testParent.parent / "bin" def mostRecentOf(base: String, names: String*) = diff --git a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala index 373da5e3db..1ba2ce61a4 100644 --- a/src/partest/scala/tools/partest/nest/ConsoleRunner.scala +++ b/src/partest/scala/tools/partest/nest/ConsoleRunner.scala @@ -128,8 +128,8 @@ class ConsoleRunner extends DirectRunner with RunnerUtils { val dir = if (!fileManager.testClasses.isEmpty) fileManager.testClassesDir - else if (fileManager.testBuild != null) - fileManager.testBuildFile + else if (fileManager.testBuildFile.isDefined) + fileManager.testBuildFile.get else fileManager.latestCompFile.getParentFile.getParentFile.getCanonicalFile NestUI.outline("Scala compiler classes in: "+dir+"\n") diff --git a/src/partest/scala/tools/partest/nest/TestFile.scala b/src/partest/scala/tools/partest/nest/TestFile.scala index 90d8db936f..e4f1d98c06 100644 --- a/src/partest/scala/tools/partest/nest/TestFile.scala +++ b/src/partest/scala/tools/partest/nest/TestFile.scala @@ -22,8 +22,7 @@ abstract class TestFile(kind: String) { val fileBase: String = basename(file.getName) def objDir = fileBase + "-" + kind + ".obj" - // @mutates settings - protected def baseSettings(settings: Settings) { + def defineSettings(settings: Settings) { settings appendToClasspath dirpath if (createOutDir) { @@ -34,10 +33,7 @@ abstract class TestFile(kind: String) { // add additional flags found in 'testname.flags' def flagsPath = Path(dir) / (fileBase + ".flags") flagsPath ifFile { _.slurp().trim } foreach (settings processArgumentString _) - } - def defineSettings(settings: Settings) { - baseSettings(settings) settings appendToClasspath fileManager.CLASSPATH } diff --git a/src/partest/scala/tools/partest/package.scala b/src/partest/scala/tools/partest/package.scala index 017974c232..d814fc4fc7 100644 --- a/src/partest/scala/tools/partest/package.scala +++ b/src/partest/scala/tools/partest/package.scala @@ -7,23 +7,21 @@ package scala.tools package object partest { import nest.NestUI - def showVMArgs { + def vmArgString = { import scala.tools.nsc.io.Process val str = Process.javaVmArguments mkString " " - NestUI.verbose("Java VM started with arguments: '%s'" format str) + "Java VM started with arguments: '%s'" format str } - def showAllProperties { + def allPropertiesString = { import collection.JavaConversions._ - for ((k, v) <- System.getProperties.toList.sorted) { - NestUI.verbose("%s -> %s".format(k, v)) - } + System.getProperties.toList.sorted map { case (k, v) => "%s -> %s\n".format(k, v) } mkString } def showAllJVMInfo { - showVMArgs - showAllProperties + NestUI.verbose(vmArgString) + NestUI.verbose(allPropertiesString) } def isPartestDebug = { diff --git a/src/scalap/scala/tools/scalap/Main.scala b/src/scalap/scala/tools/scalap/Main.scala index b9c2ab0cc8..eee689c189 100644 --- a/src/scalap/scala/tools/scalap/Main.scala +++ b/src/scalap/scala/tools/scalap/Main.scala @@ -12,7 +12,7 @@ package scala.tools.scalap import java.io.{File, PrintStream, OutputStreamWriter, ByteArrayOutputStream} import scalax.rules.scalasig._ import tools.nsc.io.AbstractFile -import tools.nsc.util.{ ClassPath, JavaClassPath } +import tools.nsc.util.{ ClassPath } import tools.util.PathResolver import ClassPath.DefaultJavaContext diff --git a/test/files/run/programmatic-main.scala b/test/files/run/programmatic-main.scala index b6fbdb9d30..0e91d219a7 100644 --- a/test/files/run/programmatic-main.scala +++ b/test/files/run/programmatic-main.scala @@ -3,8 +3,9 @@ import io.Path object Test { - val basedir = (Path(System.getProperty("scalatest.cwd")).parent / "lib").path - val baseargs = Array("-bootclasspath", basedir + "scala-library.jar", "-cp", basedir + "scala-compiler.jar") + val cwd = Option(System.getProperty("scalatest.cwd")) getOrElse "." + val basedir = (Path(cwd).parent / "lib").path + val baseargs = Array("-bootclasspath", basedir + "/scala-library.jar", "-cp", basedir + "/scala-compiler.jar") def main(args: Array[String]): Unit = { Main process (baseargs ++ Array("-Xshow-phases")) -- cgit v1.2.3