diff options
author | Paul Phillips <paulp@improving.org> | 2010-02-10 19:51:38 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2010-02-10 19:51:38 +0000 |
commit | 6e76af56f708d345bda2fff580634107945ef3fb (patch) | |
tree | 563058a03d54463fbe9139e8f9f74db23a944400 /src | |
parent | 06ae221de943d7258dfa2ffcbc6c59fe9493497f (diff) | |
download | scala-6e76af56f708d345bda2fff580634107945ef3fb.tar.gz scala-6e76af56f708d345bda2fff580634107945ef3fb.tar.bz2 scala-6e76af56f708d345bda2fff580634107945ef3fb.zip |
More work on classpaths.
to have command line options following source files, at the price of
temporarily breaking tools/pathResolver. Working my way through all the
usages of classpath in trunk zeroing in on fully consistent handling.
Review by community.
Diffstat (limited to 'src')
17 files changed, 349 insertions, 350 deletions
diff --git a/src/compiler/scala/tools/nsc/CompileClient.scala b/src/compiler/scala/tools/nsc/CompileClient.scala index d8c8caac05..4b387298f4 100644 --- a/src/compiler/scala/tools/nsc/CompileClient.scala +++ b/src/compiler/scala/tools/nsc/CompileClient.scala @@ -76,36 +76,40 @@ class StandardCompileClient { val (vmArgs, serverAdr) = normalize(args) if (version) { - Console.println(versionMsg) + Console println versionMsg return 0 } if (verbose) { - Console.println("[Server arguments: " + args.mkString("", " ", "]")) - Console.println("[VM arguments: " + vmArgs + "]") + Console println args.mkString("[Server arguments: ", " ", "]") + Console println "[VM arguments: %s]".format(vmArgs) } - val socket = if (serverAdr == "") compileSocket.getOrCreateSocket(vmArgs, !shutdown) - else compileSocket.getSocket(serverAdr) - var sawerror = false - if (socket eq null) { - if (shutdown) { - Console.println("[No compilation server running.]") - } else { - Console.println("Compilation failed.") - sawerror = true - } - } else { - val out = new PrintWriter(socket.getOutputStream(), true) - val in = new BufferedReader(new InputStreamReader(socket.getInputStream())) - out.println(compileSocket.getPassword(socket.getPort())) - out.println(args.mkString("", "\0", "")) - var fromServer = in.readLine() - while (fromServer ne null) { - if (compileSocket.errorPattern.matcher(fromServer).matches) - sawerror = true - Console.println(fromServer) - fromServer = in.readLine() - } - in.close ; out.close ; socket.close + val socket = + if (serverAdr == "") compileSocket.getOrCreateSocket(vmArgs, !shutdown) + else Some(compileSocket.getSocket(serverAdr)) + + val sawerror: Boolean = socket match { + case None => + val msg = if (shutdown) "[No compilation server running.]" else "Compilation failed." + Console println msg + !shutdown + + case Some(sock) => + var wasError = false + + sock.applyReaderAndWriter { (in, out) => + out println compileSocket.getPassword(sock.getPort()) + out println args.mkString("\0") + def loop: Unit = in.readLine() match { + case null => () + case fromServer => + if (compileSocket.errorPattern matcher fromServer matches) + wasError = true + + Console println fromServer + loop + } + } + wasError } if (sawerror) 1 else 0 } diff --git a/src/compiler/scala/tools/nsc/CompileSocket.scala b/src/compiler/scala/tools/nsc/CompileSocket.scala index bad1cc9d1c..305c4e211d 100644 --- a/src/compiler/scala/tools/nsc/CompileSocket.scala +++ b/src/compiler/scala/tools/nsc/CompileSocket.scala @@ -96,13 +96,13 @@ class CompileSocket { 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 { - case Nil => -1 - case p :: xs => - xs forall (_.delete()) - p.name.toInt - } + private def pollPort(): Int = portsDir.list match { + case it if !it.hasNext => -1 + case it => + val ret = it.next.name.toInt + it foreach (_.delete()) + ret + } /** Get the port number to which a scala compile server is connected; * If no server is running yet, then create one. @@ -140,36 +140,36 @@ class CompileSocket { 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 + * create a new daemon if necessary. Returns None if the connection * cannot be established. */ - def getOrCreateSocket(vmArgs: String, create: Boolean = true): 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 hostAdr = InetAddress.getLocalHost() - Socket(hostAdr, port).either match { - case Right(res) => + def getOrCreateSocket(vmArgs: String, create: Boolean = true): Option[Socket] = { + // try for 5 seconds + val retryDelay = 100 + val maxAttempts = (5 * 1000) / retryDelay + + def getsock(attempts: Int): Option[Socket] = attempts match { + case 0 => error("Unable to establish connection to compilation daemon") ; None + case num => + val port = if (create) getPort(vmArgs) else pollPort() + if (port < 0) return None + + Socket(InetAddress.getLocalHost(), port).either match { + case Right(socket) => info("[Connected to compilation daemon at port %d]" format port) - res - case Left(e) => - info(e.toString) + Some(socket) + case Left(err) => + info(err.toString) info("[Connecting to compilation daemon at port %d failed; re-trying...]" format port) if (attempts % 2 == 0) - portFile(port).delete // 50% chance to stop trying on this port + deletePort(port) // 50% chance to stop trying on this port - Thread.sleep(100) // delay before retrying + Thread sleep retryDelay // delay before retrying getsock(attempts - 1) } - } - getsock(nAttempts) + } + getsock(maxAttempts) } // XXX way past time for this to be central diff --git a/src/compiler/scala/tools/nsc/MainGenericRunner.scala b/src/compiler/scala/tools/nsc/MainGenericRunner.scala index 834f4d1742..0b93be582c 100644 --- a/src/compiler/scala/tools/nsc/MainGenericRunner.scala +++ b/src/compiler/scala/tools/nsc/MainGenericRunner.scala @@ -33,7 +33,11 @@ object MainGenericRunner { return errorFn("%s\n%s".format(command.usageMsg, sampleCompiler.pluginOptionsHelp)) // append the jars in ${scala.home}/lib to the classpath, as well as "." if none was given. - settings appendToClasspath PathResolver.basicScalaClassPath(settings.classpath.value == "") + val needDot = settings.classpath.value == "" + settings appendToClasspath PathResolver.genericRunnerClassPath + if (needDot) + settings appendToClasspath "." + settings.defines.applyToCurrentJVM if (settings.version.value) diff --git a/src/compiler/scala/tools/nsc/ScriptRunner.scala b/src/compiler/scala/tools/nsc/ScriptRunner.scala index 0657a61133..72533f76b5 100644 --- a/src/compiler/scala/tools/nsc/ScriptRunner.scala +++ b/src/compiler/scala/tools/nsc/ScriptRunner.scala @@ -210,15 +210,8 @@ object ScriptRunner val compArgs = coreCompArgs ::: List("-Xscript", scriptMain(settings), scriptFile) var compok = true - // XXX temporary as I started using ManagedResource not remembering it wasn't checked in. - def ManagedResource[T](x: => T) = Some(x) - - for { - socket <- ManagedResource(CompileSocket getOrCreateSocket "") - val _ = if (socket == null) return false - out <- ManagedResource(new PrintWriter(socket.getOutputStream(), true)) - in <- ManagedResource(new BufferedReader(new InputStreamReader(socket.getInputStream()))) - } { + val socket = CompileSocket getOrCreateSocket "" getOrElse (return false) + socket.applyReaderAndWriter { (in, out) => out println (CompileSocket getPassword socket.getPort) out println (compArgs mkString "\0") @@ -227,8 +220,7 @@ object ScriptRunner if (CompileSocket.errorPattern matcher fromServer matches) compok = false } - // XXX temp until managed resource is available - in.close() ; out.close() ; socket.close() + socket.close() } compok diff --git a/src/compiler/scala/tools/nsc/Settings.scala b/src/compiler/scala/tools/nsc/Settings.scala index a2176a48ea..3c24d86a99 100644 --- a/src/compiler/scala/tools/nsc/Settings.scala +++ b/src/compiler/scala/tools/nsc/Settings.scala @@ -13,6 +13,7 @@ import util.SourceFile import Settings._ import annotation.elidable import scala.tools.util.PathResolver +import scala.collection.mutable.ListBuffer class Settings(errorFn: String => Unit) extends ScalacSettings { def this() = this(Console.println) @@ -24,6 +25,7 @@ class Settings(errorFn: String => Unit) extends ScalacSettings { */ def processArguments(arguments: List[String]): (Boolean, List[String]) = { var args = arguments + val residualArgs = new ListBuffer[String] while (args.nonEmpty) { if (args.head startsWith "-") { @@ -37,10 +39,17 @@ class Settings(errorFn: String => Unit) extends ScalacSettings { else if (args.head == "") { // discard empties, sometimes they appear because of ant or etc. args = args.tail } - else return (checkDependencies, args) + else { + // XXX have to rework MainGenericRunner. If we return early we break + // the ability to have command line options follow source files, but if + // we don't the command line is processed too early. + // return (checkDependencies, args) + residualArgs += args.head + args = args.tail + } } - (checkDependencies, args) + (checkDependencies, residualArgs.toList) } def processArgumentString(params: String) = processArguments(splitParams(params)) diff --git a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala index 0d2f9a13d1..a62b8e5e58 100644 --- a/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala +++ b/src/compiler/scala/tools/nsc/backend/JavaPlatform.scala @@ -19,11 +19,7 @@ trait JavaPlatform extends Platform[AbstractFile] { if (isInlinerOn) new JavaContext else DefaultJavaContext - new JavaClassPath( - settings.bootclasspath.value, settings.extdirs.value, - settings.classpath.value, settings.sourcepath.value, - settings.Xcodebase.value, context - ) + PathResolver.fromSettings(settings, context) } def rootLoader = new loaders.JavaPackageLoader(classPath) diff --git a/src/compiler/scala/tools/nsc/io/AbstractFile.scala b/src/compiler/scala/tools/nsc/io/AbstractFile.scala index 15a05aa3fc..af93cafb68 100644 --- a/src/compiler/scala/tools/nsc/io/AbstractFile.scala +++ b/src/compiler/scala/tools/nsc/io/AbstractFile.scala @@ -109,6 +109,9 @@ abstract class AbstractFile extends AnyRef with Iterable[AbstractFile] { if (file ne null) file.exists else true + /** Does this abstract file represent something which can contain classfiles? */ + def isClassContainer = isDirectory || Path.isJarOrZip(sfile) + /** Create a file on disk, if one does not exist already. */ def create: Unit diff --git a/src/compiler/scala/tools/nsc/io/Path.scala b/src/compiler/scala/tools/nsc/io/Path.scala index 39fba8bcb2..2b7f2620d6 100644 --- a/src/compiler/scala/tools/nsc/io/Path.scala +++ b/src/compiler/scala/tools/nsc/io/Path.scala @@ -141,6 +141,8 @@ class Path private[io] (val jfile: JFile) } // compares against extension in a CASE INSENSITIVE way. def hasExtension(other: String) = extension.toLowerCase == other.toLowerCase + // returns the filename without the extension. + def stripExtension: String = name stripSuffix ("." + extension) // conditionally execute def ifFile[T](f: File => T): Option[T] = if (isFile) Some(f(toFile)) else None diff --git a/src/compiler/scala/tools/nsc/io/Socket.scala b/src/compiler/scala/tools/nsc/io/Socket.scala index 18ccbda7a2..e883c71b8e 100644 --- a/src/compiler/scala/tools/nsc/io/Socket.scala +++ b/src/compiler/scala/tools/nsc/io/Socket.scala @@ -6,7 +6,7 @@ package scala.tools.nsc package io -import java.io.{ IOException } +import java.io.{ IOException, InputStreamReader, BufferedReader, PrintWriter } import java.net.{ URL, MalformedURLException } import java.net.{ InetAddress, Socket => JSocket } import scala.util.control.Exception._ @@ -31,4 +31,16 @@ class Socket(jsocket: JSocket) { def getInputStream() = jsocket.getInputStream() def getPort() = jsocket.getPort() def close() = jsocket.close() + + /** Creates an InputStream and applies the closure, automatically closing it on completion. + */ + def applyReaderAndWriter[T](f: (BufferedReader, PrintWriter) => T): T = { + val out = new PrintWriter(getOutputStream(), true) + val in = new BufferedReader(new InputStreamReader(getInputStream())) + try f(in, out) + finally { + in.close() + out.close() + } + } }
\ No newline at end of file diff --git a/src/compiler/scala/tools/nsc/io/Streamable.scala b/src/compiler/scala/tools/nsc/io/Streamable.scala index a55028bc25..ff4520e3ca 100644 --- a/src/compiler/scala/tools/nsc/io/Streamable.scala +++ b/src/compiler/scala/tools/nsc/io/Streamable.scala @@ -101,7 +101,7 @@ object Streamable */ def bufferedReader(codec: Codec = getCodec()) = new BufferedReader(reader(codec)) - /** Creates an InputStream and applies the closure, automatically closing it on completion. + /** Creates a BufferedReader and applies the closure, automatically closing it on completion. */ def applyReader[T](f: BufferedReader => T): T = { val in = bufferedReader() diff --git a/src/compiler/scala/tools/nsc/util/ClassPath.scala b/src/compiler/scala/tools/nsc/util/ClassPath.scala index 6be887396a..3f6bed5227 100644 --- a/src/compiler/scala/tools/nsc/util/ClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/ClassPath.scala @@ -14,6 +14,7 @@ import java.net.URL import scala.collection.mutable.{ListBuffer, ArrayBuffer, HashSet => MutHashSet} import io.{ File, Directory, Path, AbstractFile } import scala.tools.util.StringOps.splitWhere +import Path.isJarOrZip /** <p> * This module provides star expansion of '-classpath' option arguments, behaves the same as @@ -53,16 +54,10 @@ object ClassPath { if (expandStar) splitPath(path) flatMap expandS else splitPath(path) - /** Expand dir out to contents */ + /** Expand dir out to contents, a la extdir */ def expandDir(extdir: String): List[String] = { - for { - dir <- Option(AbstractFile getDirectory extdir).toList - file <- dir - if file.isDirectory || (file hasExtension "jar") || (file hasExtension "zip") - } - yield { - (dir.sfile / file.name).path - } + val dir = Option(AbstractFile getDirectory extdir) getOrElse (return Nil) + dir filter (_.isClassContainer) map (dir.sfile / _.name path) toList } /** A useful name filter. */ @@ -85,6 +80,35 @@ object ClassPath { /** From the representation to its identifier. */ def toBinaryName(rep: T): String + + /** Create a new classpath based on the abstract file. + */ + def newClassPath(file: AbstractFile): ClassPath[T] + + /** Creators for sub classpaths which preserve this context. + */ + def sourcesInPath(path: String): List[ClassPath[T]] = + for (file <- expandPath(path, false) ; dir <- Option(AbstractFile getDirectory file)) yield + new SourcePath[T](dir, this) + + def contentsOfDirsInPath(path: String): List[ClassPath[T]] = + for (dir <- expandPath(path, false) ; name <- expandDir(dir) ; entry <- Option(AbstractFile getDirectory name)) yield + newClassPath(entry) + + def classesAtAllURLS(path: String): List[ClassPath[T]] = + (path split " ").toList flatMap classesAtURL + + def classesAtURL(spec: String) = + for (url <- specToURL(spec).toList ; location <- Option(AbstractFile getURL url)) yield + newClassPath(location) + + def classesInExpandedPath(path: String) = classesInPathImpl(path, true) + def classesInPath(path: String) = classesInPathImpl(path, false) + + // Internal + private def classesInPathImpl(path: String, expand: Boolean) = + for (file <- expandPath(path, expand) ; dir <- Option(AbstractFile getDirectory file)) yield + newClassPath(dir) } class JavaContext extends ClassPathContext[AbstractFile] { @@ -92,6 +116,7 @@ object ClassPath { assert(rep.name endsWith ".class", rep.name) rep.name dropRight 6 } + def newClassPath(dir: AbstractFile) = new DirectoryClassPath(dir, this) } object DefaultJavaContext extends JavaContext { @@ -133,10 +158,6 @@ abstract class ClassPath[T] { val packages: List[ClassPath[T]] val sourcepaths: List[AbstractFile] - /** Creation controlled so context is retained. - */ - def createSourcePath(dir: AbstractFile) = new SourcePath[T](dir, context) - /** * Represents classes which can be loaded with a ClassfileLoader/MSILTypeLoader * and / or a SourcefileLoader. @@ -180,25 +201,21 @@ abstract class ClassPath[T] { } } -trait AbstractFileClassPath extends ClassPath[AbstractFile] { - def createDirectoryPath(dir: AbstractFile): DirectoryClassPath = new DirectoryClassPath(dir, context) -} - /** * A Classpath containing source files */ class SourcePath[T](dir: AbstractFile, val context: ClassPathContext[T]) extends ClassPath[T] { def name = dir.name + val sourcepaths: List[AbstractFile] = List(dir) lazy val classes: List[ClassRep] = dir partialMap { case f if !f.isDirectory && validSourceFile(f.name) => ClassRep(None, Some(f)) } toList lazy val packages: List[SourcePath[T]] = dir partialMap { - case f if f.isDirectory && validPackage(f.name) => createSourcePath(f) + case f if f.isDirectory && validPackage(f.name) => new SourcePath[T](f, context) } toList - val sourcepaths: List[AbstractFile] = List(dir) override def toString() = "sourcepath: "+ dir.toString() } @@ -206,18 +223,18 @@ class SourcePath[T](dir: AbstractFile, val context: ClassPathContext[T]) extends /** * A directory (or a .jar file) containing classfiles and packages */ -class DirectoryClassPath(dir: AbstractFile, val context: ClassPathContext[AbstractFile]) extends AbstractFileClassPath { +class DirectoryClassPath(dir: AbstractFile, val context: ClassPathContext[AbstractFile]) extends ClassPath[AbstractFile] { def name = dir.name + val sourcepaths: List[AbstractFile] = Nil lazy val classes: List[ClassRep] = dir partialMap { case f if !f.isDirectory && validClassFile(f.name) => ClassRep(Some(f), None) } toList lazy val packages: List[DirectoryClassPath] = dir partialMap { - case f if f.isDirectory && validPackage(f.name) => createDirectoryPath(f) + case f if f.isDirectory && validPackage(f.name) => new DirectoryClassPath(f, context) } toList - val sourcepaths: List[AbstractFile] = Nil override def toString() = "directory classpath: "+ dir.toString() } @@ -225,12 +242,13 @@ class DirectoryClassPath(dir: AbstractFile, val context: ClassPathContext[Abstra /** * A classpath unifying multiple class- and sourcepath entries. */ -abstract class MergedClassPath[T] extends ClassPath[T] { - outer => - - protected val entries: List[ClassPath[T]] +class MergedClassPath[T]( + protected val entries: List[ClassPath[T]], + val context: ClassPathContext[T]) +extends ClassPath[T] { def name = entries.head.name + lazy val sourcepaths: List[AbstractFile] = entries flatMap (_.sourcepaths) lazy val classes: List[AnyClassRep] = { val cls = new ListBuffer[AnyClassRep] @@ -264,18 +282,13 @@ abstract class MergedClassPath[T] extends ClassPath[T] { pkg.toList } - lazy val sourcepaths: List[AbstractFile] = entries flatMap (_.sourcepaths) - - private def addPackage(to: ClassPath[T], pkg: ClassPath[T]) = newMergedClassPath(to match { - case cp: MergedClassPath[_] => cp.entries :+ pkg - case _ => List(to, pkg) - }) - - private def newMergedClassPath(entrs: List[ClassPath[T]]): MergedClassPath[T] = - new MergedClassPath[T] { - protected val entries = entrs - val context = outer.context + private def addPackage(to: ClassPath[T], pkg: ClassPath[T]) = { + val newEntries = to match { + case cp: MergedClassPath[_] => cp.entries :+ pkg + case _ => List(to, pkg) } + new MergedClassPath[T](newEntries, context) + } override def toString() = "merged classpath "+ entries.mkString("(", "\n", ")") } @@ -285,47 +298,6 @@ abstract class MergedClassPath[T] extends ClassPath[T] { * as AbstractFile. nsc.io.ZipArchive is used to view zip/jar archives as directories. */ class JavaClassPath( - boot: String, - ext: String, - user: String, - source: String, - Xcodebase: String, - val context: JavaContext -) extends MergedClassPath[AbstractFile] with AbstractFileClassPath { - - protected val entries: List[ClassPath[AbstractFile]] = assembleEntries() - - private def assembleEntries(): List[ClassPath[AbstractFile]] = { - import ClassPath._ - val etr = new ListBuffer[ClassPath[AbstractFile]] - - def addFilesInPath(path: String, expand: Boolean, ctr: AbstractFile => ClassPath[AbstractFile]): Unit = - for (fileName <- expandPath(path, expand) ; file <- Option(AbstractFile getDirectory fileName)) yield - etr += ctr(file) - - def addContentsOfDir(path: String): Unit = - for (name <- expandDir(path) ; archive <- Option(AbstractFile getDirectory name)) - etr += createDirectoryPath(archive) - - def addContentsOfURL(spec: String): Unit = - for (url <- specToURL(spec) ; archive <- Option(AbstractFile getURL url)) - etr += createDirectoryPath(archive) - - // 1. Boot classpath - addFilesInPath(boot, false, createDirectoryPath) - - // 2. Ext classpath - addContentsOfDir(ext) - - // 3. User classpath - addFilesInPath(user, true, createDirectoryPath) - - // 4. Codebase entries (URLs) - Xcodebase.trim split " " foreach addContentsOfURL - - // 5. Source path - addFilesInPath(source, false, createSourcePath) - - etr.toList - } -} + containers: List[List[ClassPath[AbstractFile]]], + context: JavaContext) +extends MergedClassPath[AbstractFile](containers.flatten, context) { } diff --git a/src/compiler/scala/tools/nsc/util/MsilClassPath.scala b/src/compiler/scala/tools/nsc/util/MsilClassPath.scala index aacaf1b7ad..2de334c370 100644 --- a/src/compiler/scala/tools/nsc/util/MsilClassPath.scala +++ b/src/compiler/scala/tools/nsc/util/MsilClassPath.scala @@ -37,9 +37,58 @@ object MsilClassPath { class MsilContext extends ClassPathContext[MSILType] { def toBinaryName(rep: MSILType) = rep.Name + def newClassPath(assemFile: AbstractFile) = new AssemblyClassPath(MsilClassPath collectTypes assemFile, "", this) + } + + private def assembleEntries(ext: String, user: String, source: String, context: MsilContext): List[ClassPath[MSILType]] = { + import ClassPath._ + val etr = new ListBuffer[ClassPath[MSILType]] + val names = new MutHashSet[String] + + // 1. Assemblies from -Xassem-extdirs + for (dirName <- expandPath(ext, expandStar = false)) { + val dir = AbstractFile.getDirectory(dirName) + if (dir ne null) { + for (file <- dir) { + val name = file.name.toLowerCase + if (name.endsWith(".dll") || name.endsWith(".exe")) { + names += name + etr += context.newClassPath(file) + } + } + } + } + + // 2. Assemblies from -Xassem-path + for (fileName <- expandPath(user, expandStar = false)) { + val file = AbstractFile.getFile(fileName) + if (file ne null) { + val name = file.name.toLowerCase + if (name.endsWith(".dll") || name.endsWith(".exe")) { + names += name + etr += context.newClassPath(file) + } + } + } + + def check(n: String) { + if (!names.contains(n)) + throw new AssertionError("Cannot find assembly "+ n + + ". Use -Xassem-extdirs or -Xassem-path to specify its location") + } + check("mscorlib.dll") + check("scalaruntime.dll") + + // 3. Source path + for (dirName <- expandPath(source, expandStar = false)) { + val file = AbstractFile.getDirectory(dirName) + if (file ne null) etr += new SourcePath[MSILType](file, context) + } + + etr.toList } } -import MsilClassPath.MsilContext +import MsilClassPath._ /** * A assembly file (dll / exe) containing classes and namespaces @@ -102,57 +151,8 @@ class AssemblyClassPath(types: Array[MSILType], namespace: String, val context: * The classpath when compiling with target:msil. Binary files are represented as * MSILType values. */ -class MsilClassPath(ext: String, user: String, source: String, val context: MsilContext) extends MergedClassPath[MSILType] { - protected val entries: List[ClassPath[MSILType]] = assembleEntries() - - def createAssemblyPath(assemFile: AbstractFile) = - new AssemblyClassPath(MsilClassPath collectTypes assemFile, "", context) - - private def assembleEntries(): List[ClassPath[MSILType]] = { - import ClassPath._ - val etr = new ListBuffer[ClassPath[MSILType]] - val names = new MutHashSet[String] - - // 1. Assemblies from -Xassem-extdirs - for (dirName <- expandPath(ext, expandStar = false)) { - val dir = AbstractFile.getDirectory(dirName) - if (dir ne null) { - for (file <- dir) { - val name = file.name.toLowerCase - if (name.endsWith(".dll") || name.endsWith(".exe")) { - names += name - etr += createAssemblyPath(file) - } - } - } - } - - // 2. Assemblies from -Xassem-path - for (fileName <- expandPath(user, expandStar = false)) { - val file = AbstractFile.getFile(fileName) - if (file ne null) { - val name = file.name.toLowerCase - if (name.endsWith(".dll") || name.endsWith(".exe")) { - names += name - etr += createAssemblyPath(file) - } - } - } - - def check(n: String) { - if (!names.contains(n)) - throw new AssertionError("Cannot find assembly "+ n + - ". Use -Xassem-extdirs or -Xassem-path to specify its location") - } - check("mscorlib.dll") - check("scalaruntime.dll") - - // 3. Source path - for (dirName <- expandPath(source, expandStar = false)) { - val file = AbstractFile.getDirectory(dirName) - if (file ne null) etr += new SourcePath[MSILType](file, context) - } - - etr.toList - } -}
\ No newline at end of file +class MsilClassPath(ext: String, user: String, source: String, context: MsilContext) + extends MergedClassPath[MSILType]( + MsilClassPath.assembleEntries(ext, user, source, context), + context + ) diff --git a/src/compiler/scala/tools/util/PathResolver.scala b/src/compiler/scala/tools/util/PathResolver.scala index 3aca9c3ee0..72dcac36fe 100644 --- a/src/compiler/scala/tools/util/PathResolver.scala +++ b/src/compiler/scala/tools/util/PathResolver.scala @@ -10,7 +10,7 @@ import java.net.{ URL, MalformedURLException } import nsc.{ Settings } import nsc.util.{ ClassPath, JavaClassPath, ScalaClassLoader } import nsc.io.{ File, Directory, Path } -import ClassPath.DefaultJavaContext +import ClassPath.{ JavaContext, DefaultJavaContext } import File.{ pathSeparator } import PartialFunction.condOpt @@ -51,6 +51,7 @@ object PathResolver { def javaExtDirs = propOrElse("java.ext.dirs", "") def userHome = propOrElse("user.home", "") def scalaHome = propOrElse("scala.home", "") + def scalaExtDirs = propOrElse("scala.ext.dirs", "") // XXX not in spec def classPath = List(classPathProp, classPathEnv) find (_ != "") getOrElse "." @@ -64,8 +65,8 @@ object PathResolver { | userHome = %s | scalaHome = %s |}""".trim.stripMargin.format( - classPathEnv, toolPathEnv, ppcp(classPathProp), ppcp(javaBootClassPath), - javaExtDirs, userHome, scalaHome + ppcp(classPathEnv), ppcp(toolPathEnv), ppcp(classPathProp), ppcp(javaBootClassPath), + ppcp(javaExtDirs), userHome, scalaHome ) } @@ -91,13 +92,15 @@ object PathResolver { if (scalaLibJar.isFile) Some(scalaLibDir) else if (scalaLibClassDir.isDirectory) Some(scalaClassesDir) else None - def scalaLibPath = scalaLibFound map (_.path) getOrElse "" - def scalaExtDirs = scalaLibFound map (_.path) getOrElse "" - def expandedScalaExtDirs = if (scalaExtDirs == "") "" else expandToContents(scalaExtDirs) - def expandedClassPath = expandToPath(Environment.classPath) + def scalaBootClassPath = scalaLibFound map expandToContents getOrElse "" + def scalaExtDirs = Environment.scalaExtDirs + def scalaToolPath = Environment.toolPathEnv match { + case "" => scalaBootClassPath + case x => expandToPath(x.replaceAll("""\$\{SCALA_HOME\}""", scalaHome)) + } - val pluginSearchPath = List("misc", "scala-devel", "plugins") + def pluginSearchPath = List("misc", "scala-devel", "plugins") def scalaPluginDir = pluginSearchPath map (scalaHomeDir / _) find (_.isDirectory) map (_.path) getOrElse "" // The class path that a runner script uses to interpret a program is called the “execution class path”. @@ -114,21 +117,17 @@ object PathResolver { // This path may contain absolute locations, or locations relative to Scala's home by using // the shell variable ${SCALA_HOME} in the path. // * The class path formed by all JAR and ZIP files and all folders in Scala's home lib folder. - Environment.toolPathEnv match { - case "" => Defaults.expandedScalaExtDirs - case x => expandToPath(translateScalaHome(x)) - } + scalaToolPath ) override def toString = """ |object Defaults { | scalaHome = %s - | scalaLibFound = %s + | scalaLibFound = %s + | scalaBootClassPath = %s | scalaPluginDir = %s - | expandedClassPath = %s |}""".trim.stripMargin.format( - scalaHome, scalaLibFound, scalaPluginDir, - ppcp(expandedScalaExtDirs), ppcp(expandedClassPath) + scalaHome, scalaLibFound, ppcp(scalaBootClassPath), scalaPluginDir ) } @@ -136,28 +135,53 @@ object PathResolver { /** The original logic of MainGenericRunner. */ - def basicScalaClassPath(includeCwd: Boolean): String = { + def genericRunnerClassPath: String = { // this is to make the interpreter work when running without the scala script // (e.g. from eclipse). Before, "java.class.path" was added to the user classpath - // in Settings; this was changed to match the behavior of Sun's javac. - def maincp = - if (Environment.scalaHome == "") Defaults.expandedClassPath - else Defaults.expandedScalaExtDirs - def dotcp = if (includeCwd) "." else "" + // in Settings; this was changed to match the behavior of Sun's javac. So the + // classpath generated here consist of only the scala lib/* jars if scalaHome + // can be found, and the whole user classpath otherwise. + if (Environment.scalaHome == "") Environment.classPath + else Defaults.scalaBootClassPath + } + + private def contextFromSettings(s: Settings) = + if (s.inline.value) new JavaContext else DefaultJavaContext + + def fromArgumentString(argString: String): JavaClassPath = + fromArgumentList(argString.trim split """\s+""" toList) - joincp(Seq(maincp, dotcp)) + def fromArgumentList(args: List[String]): JavaClassPath = { + val settings = new Settings() + settings processArguments args + fromSettings(settings, contextFromSettings(settings)) } - /** XXX not yet used. - */ - def toJavaClassPath(settings: Settings): JavaClassPath = { - new JavaClassPath( - settings.bootclasspath.value, settings.extdirs.value, - settings.classpath.value, settings.sourcepath.value, - settings.Xcodebase.value, DefaultJavaContext + def fromSettings(settings: Settings): JavaClassPath = + fromSettings(settings, contextFromSettings(settings)) + + def fromSettings(settings: Settings, context: JavaContext): JavaClassPath = { + import context._ + import settings._ + + val sources = List( + classesInPath(bootclasspath.value), // -bootclasspath multiple entries, no expansion + contentsOfDirsInPath(extdirs.value), // -extdirs multiple dirs, each expands to contents + classesInExpandedPath(classpath.value), // -classpath multiple entries, first expanding *s + classesAtAllURLS(Xcodebase.value), // -Xcodebase multiple URLs + sourcesInPath(sourcepath.value) // -sourcepath multiple source entries, no expansion ) + + if (Ylogcp.value) + Console.println("Created JavaClassPath from:\n" + (new PathResolver(settings)).Calculated) + + new JavaClassPath(sources, context) } + def fromPathString(path: String): JavaClassPath = fromPathString(path, DefaultJavaContext) + def fromPathString(path: String, context: JavaContext): JavaClassPath = + new JavaClassPath(List(context.classesInExpandedPath(path)), context) + /** With no arguments, show the interesting values in Environment and Defaults. * If there are arguments, show those in Calculated as if those options had been * given to a scala runner. @@ -266,7 +290,7 @@ class PathResolver(settings: Settings) { object Calculated { def javaBootClassPath = cmdLineOrElse("javabootclasspath", Environment.javaBootClassPath) def javaExtDirs = cmdLineOrElse("javaextdirs", Environment.javaExtDirs) - def scalaBootClassPath = cmdLineOrElse("bootclasspath", Defaults.scalaLibPath) + def scalaBootClassPath = cmdLineOrElse("bootclasspath", Defaults.scalaBootClassPath) def scalaExtDirs = cmdLineOrElse("extdirs", Defaults.scalaExtDirs) def sourcePath = cmdLineOrElse("sourcepath", "") def codeBase = cmdLineOrElse("Ycodebase", "") // XXX diff --git a/src/compiler/scala/tools/util/StringOps.scala b/src/compiler/scala/tools/util/StringOps.scala index 21ece6490f..77fc6f8700 100644 --- a/src/compiler/scala/tools/util/StringOps.scala +++ b/src/compiler/scala/tools/util/StringOps.scala @@ -33,6 +33,14 @@ object StringOps { def words(str: String): List[String] = decompose(str, ' ') + def stripPrefixOpt(str: String, prefix: String): Option[String] = + if (str startsWith prefix) Some(str drop prefix.length) + else None + + def stripSuffixOpt(str: String, suffix: String): Option[String] = + if (str endsWith suffix) Some(str dropRight suffix.length) + else None + def splitWhere(str: String, f: Char => Boolean, doDropIndex: Boolean = false): Option[(String, String)] = splitAt(str, str indexWhere f, doDropIndex) diff --git a/src/library/scala/runtime/ScalaRunTime.scala b/src/library/scala/runtime/ScalaRunTime.scala index 28f8060372..07da120399 100644 --- a/src/library/scala/runtime/ScalaRunTime.scala +++ b/src/library/scala/runtime/ScalaRunTime.scala @@ -192,7 +192,11 @@ object ScalaRunTime { // Node extends NodeSeq extends Seq[Node] strikes again case x: Node => x toString case x: AnyRef if isArray(x) => WrappedArray make x map stringOf mkString ("Array(", ", ", ")") - case x: Traversable[_] => x map stringOf mkString (x.stringPrefix + "(", ", ", ")") + case x: Traversable[_] => + // Some subclasses of AbstractFile implement Iterable, then throw an + // exception if you call iterator. What a world. + if (x.getClass.getName startsWith "scala.tools.nsc.io") x.toString + else x map stringOf mkString (x.stringPrefix + "(", ", ", ")") case x => x toString } } diff --git a/src/partest/scala/tools/partest/nest/Worker.scala b/src/partest/scala/tools/partest/nest/Worker.scala index 1907c65932..bd742a5b13 100644 --- a/src/partest/scala/tools/partest/nest/Worker.scala +++ b/src/partest/scala/tools/partest/nest/Worker.scala @@ -12,17 +12,17 @@ import java.io._ import java.net.{URLClassLoader, URL} import java.util.{Timer, TimerTask} -import scala.tools.nsc.{ObjectRunner, GenericRunnerCommand} -import scala.tools.nsc.io -import io.{AbstractFile, PlainFile} +import scala.tools.nsc.{ ObjectRunner, Settings, CompilerCommand, Global } +import scala.tools.nsc.io.{ AbstractFile, PlainFile, Path, Directory, File => SFile } +import scala.tools.nsc.reporters.ConsoleReporter +import scala.tools.nsc.util.FakePos import scala.actors.{Actor, Exit, TIMEOUT} import scala.actors.Actor._ import scala.tools.scalap.scalax.rules.scalasig.{ByteCode, ClassFileParser, ScalaSigAttributeParsers} -import scala.collection.immutable.{Map => ImmMap} +import scala.collection.immutable.{ HashMap, Map => ImmMap } import scala.collection.Map -import scala.collection.immutable.HashMap import scala.tools.nsc.interactive.{BuildManager, RefinedBuildManager} @@ -43,24 +43,21 @@ class LogFile(parent: File, child: String) extends File(parent, child) { class Worker(val fileManager: FileManager) extends Actor { import fileManager._ - import scala.tools.nsc.{Settings, CompilerCommand, Global} - import scala.tools.nsc.reporters.ConsoleReporter - import scala.tools.nsc.util.FakePos var reporter: ConsoleReporter = _ val timer = new Timer - def error(msg: String) { - reporter.error(FakePos("scalac"), - msg + "\n scalac -help gives more information") - } + def error(msg: String): Unit = reporter.error( + FakePos("scalac"), + msg + "\n scalac -help gives more information" + ) def act() { react { case RunTests(kind, files) => NestUI.verbose("received "+files.length+" to test") val master = sender - runTests(kind, files){ results => + runTests(kind, files) { results => master ! Results(results, createdLogFiles, createdOutputDirs) } } @@ -88,7 +85,7 @@ class Worker(val fileManager: FileManager) extends Actor { file.getAbsolutePath.substring(filesPathLen) } } - NestUI.normal("[...]"+name+(List.fill(totalWidth-name.length)(' ')).mkString, printer) + NestUI.normal("[...]%s%s".format(name, " " * (totalWidth - name.length)), printer) } def printInfoEnd(success: Boolean, printer: PrintWriter) { @@ -105,17 +102,17 @@ class Worker(val fileManager: FileManager) extends Actor { } var log = "" - var createdLogFiles: List[LogFile] = List() - var createdOutputDirs: List[File] = List() + var createdLogFiles: List[LogFile] = Nil + var createdOutputDirs: List[File] = Nil def createLogFile(file: File, kind: String): LogFile = { val logFile = fileManager.getLogFile(file, kind) - createdLogFiles = logFile :: createdLogFiles + createdLogFiles ::= logFile logFile } def createOutputDir(dir: File, fileBase: String, kind: String): File = { - val outDir = io.Path(dir) / io.Directory("%s-%s.obj".format(fileBase, kind)) + val outDir = Path(dir) / Directory("%s-%s.obj".format(fileBase, kind)) outDir.createDirectory() createdOutputDirs ::= outDir.jfile outDir.jfile @@ -124,14 +121,15 @@ class Worker(val fileManager: FileManager) extends Actor { /* Note: not yet used/tested. */ def execTestObjectRunner(file: File, outDir: File, logFile: File) { val consFM = new ConsoleFileManager - import consFM.{latestCompFile, latestLibFile, latestActFile, - latestPartestFile} - val classpath: List[URL] = - outDir.toURI.toURL :: - //List(file.getParentFile.toURI.toURL) ::: - List(latestCompFile.toURI.toURL, latestLibFile.toURI.toURL, latestActFile.toURI.toURL, latestPartestFile.toURI.toURL) ::: - ((CLASSPATH split File.pathSeparatorChar).toList map (x => new File(x).toURI.toURL)) + val classpath: List[URL] = { + import consFM.{ latestCompFile, latestLibFile, latestActFile, latestPartestFile } + val units = ( + List(outDir, latestCompFile, latestLibFile, latestActFile, latestPartestFile) ::: + ((CLASSPATH split File.pathSeparatorChar).toList map (x => new File(x))) + ) + units map (_.toURI.toURL) + } NestUI.verbose("ObjectRunner classpath: "+classpath) @@ -278,31 +276,27 @@ class Worker(val fileManager: FileManager) extends Actor { } } - def existsCheckFile(dir: File, fileBase: String, kind: String) = { - val checkFile = { - val chkFile = new File(dir, fileBase + ".check") - if (chkFile.isFile) - chkFile - else - new File(dir, fileBase + "-" + kind + ".check") - } - checkFile.exists && checkFile.canRead + def getCheckFile(dir: File, fileBase: String, kind: String) = { + def chkFile(s: String) = Directory(dir) / "%s%s.check".format(fileBase, s) + val checkFile = if (chkFile("").isFile) chkFile("") else chkFile("-" + kind) + + if (checkFile.exists && checkFile.canRead) Some(checkFile) else None } - def compareOutput(dir: File, fileBase: String, kind: String, logFile: File): String = { - def getCheckFile(s: String) = { - val f = io.Path(dir) / io.File("%s%s.check".format(fileBase, s)) - if (f.isFile && f.canRead) Some(f) else None - } + def existsCheckFile(dir: File, fileBase: String, kind: String) = + getCheckFile(dir, fileBase, kind).isDefined + def compareOutput(dir: File, fileBase: String, kind: String, logFile: File): String = // if check file exists, compare with log file - (getCheckFile("") orElse getCheckFile("-" + kind)) match { + getCheckFile(dir, fileBase, kind) match { case Some(f) => fileManager.compareFiles(logFile, f.jfile) case _ => file2String(logFile) } - } - def file2String(logFile: File) = io.File(logFile).slurp() + def file2String(logFile: File) = SFile(logFile).slurp() + def isJava(f: File) = SFile(f) hasExtension "java" + def isScala(f: File) = SFile(f) hasExtension "scala" + def isJavaOrScala(f: File) = isJava(f) || isScala(f) /** Runs a list of tests. * @@ -316,6 +310,11 @@ class Worker(val fileManager: FileManager) extends Actor { var diff = "" var log = "" + def fail(what: Any) { + NestUI.verbose("scalac: compilation of "+what+" failed\n") + succeeded = false + } + /** 1. Creates log file and output directory. * 2. Runs <code>script</code> function, providing log file and * output directory as arguments. @@ -356,57 +355,40 @@ class Worker(val fileManager: FileManager) extends Actor { } def compileFilesIn(dir: File, kind: String, logFile: File, outDir: File) { - val testFiles = dir.listFiles.toList + val testFiles = dir.listFiles.toList filter isJavaOrScala - val groups = for (i <- 0 to 9) yield testFiles filter { f => - f.getName.endsWith("_"+i+".java") || - f.getName.endsWith("_"+i+".scala") } - - val noSuffix = testFiles filter { f => - !groups.exists(_ contains f) && ( - f.getName.endsWith(".java") || - f.getName.endsWith(".scala")) } + def isInGroup(f: File, num: Int) = SFile(f).stripExtension endsWith ("_" + num) + val groups = (0 to 9).toList map (num => testFiles filter (f => isInGroup(f, num))) + val noGroupSuffix = testFiles -- groups.flatten def compileGroup(g: List[File]) { - val scalaFiles = g.filter(_.getName.endsWith(".scala")) - val javaFiles = g.filter(_.getName.endsWith(".java")) - - if (!scalaFiles.isEmpty && - !compileMgr.shouldCompile(outDir, - javaFiles ::: scalaFiles, - kind, logFile)) { - NestUI.verbose("scalac: compilation of "+g+" failed\n") - succeeded = false + val (scalaFiles, javaFiles) = g partition isScala + + if (scalaFiles.nonEmpty) { + if (!compileMgr.shouldCompile(outDir, javaFiles ::: scalaFiles, kind, logFile)) + fail(g) } - if (succeeded && !javaFiles.isEmpty) { + if (succeeded && javaFiles.nonEmpty) { succeeded = javac(outDir, javaFiles, logFile) - if (succeeded && !scalaFiles.isEmpty - && !compileMgr.shouldCompile(outDir, - scalaFiles, - kind, logFile)) { - NestUI.verbose("scalac: compilation of "+scalaFiles+" failed\n") - succeeded = false - } + if (succeeded && scalaFiles.nonEmpty && !compileMgr.shouldCompile(outDir, scalaFiles, kind, logFile)) + fail(scalaFiles) } } - if (!noSuffix.isEmpty) - compileGroup(noSuffix) - for (grp <- groups) { - if (succeeded) - compileGroup(grp) - } + if (noGroupSuffix.nonEmpty) + compileGroup(noGroupSuffix) + + groups foreach (grp => if (succeeded) compileGroup(grp)) } def failCompileFilesIn(dir: File, kind: String, logFile: File, outDir: File) { - val testFiles = dir.listFiles.toList - val javaFiles = testFiles.filter(_.getName.endsWith(".java")) - val scalaFiles = testFiles.filter(_.getName.endsWith(".scala")) - if (!(scalaFiles.isEmpty && javaFiles.isEmpty) && - !compileMgr.shouldFailCompile(outDir, javaFiles ::: scalaFiles, kind, logFile)) { - NestUI.verbose("compilation of "+scalaFiles+" failed\n") - succeeded = false + val testFiles = dir.listFiles.toList + val sourceFiles = testFiles filter isJavaOrScala + + if (sourceFiles.nonEmpty) { + if (!compileMgr.shouldFailCompile(outDir, sourceFiles, kind, logFile)) + fail(testFiles filter isScala) } } @@ -415,8 +397,7 @@ class Worker(val fileManager: FileManager) extends Actor { if (file.isDirectory) { compileFilesIn(file, kind, logFile, outDir) } else if (!compileMgr.shouldCompile(List(file), kind, logFile)) { - NestUI.verbose("compilation of "+file+" failed\n") - succeeded = false + fail(file) } if (succeeded) { // run test val fileBase = basename(file.getName) @@ -445,8 +426,7 @@ class Worker(val fileManager: FileManager) extends Actor { if (file.isDirectory) { compileFilesIn(file, kind, logFile, outDir) } else if (!compileMgr.shouldCompile(List(file), kind, logFile)) { - NestUI.verbose("compilation of "+file+" failed\n") - succeeded = false + fail(file) } if (succeeded) { val consFM = new ConsoleFileManager @@ -468,9 +448,9 @@ class Worker(val fileManager: FileManager) extends Actor { ObjectRunner.run(classpath, "Test", Nil) } - NestUI.verbose(io.File(logFile).slurp()) + NestUI.verbose(SFile(logFile).slurp()) // obviously this must be improved upon - succeeded = io.File(logFile).lines() forall (_ contains " OK") + succeeded = SFile(logFile).lines() forall (_ contains " OK") } }) @@ -479,8 +459,7 @@ class Worker(val fileManager: FileManager) extends Actor { if (file.isDirectory) { compileFilesIn(file, kind, logFile, outDir) } else if (!compileMgr.shouldCompile(List(file), kind, logFile)) { - NestUI.verbose("compilation of "+file+" failed\n") - succeeded = false + fail(file) } }) @@ -1089,19 +1068,13 @@ class Worker(val fileManager: FileManager) extends Actor { } } - private def filesToSet(pre: String, fs: List[String]): Set[AbstractFile] = { - Set.empty ++ fs.map( s => AbstractFile.getFile(pre + s)).filter(_ != null) - } + private def filesToSet(pre: String, fs: List[String]): Set[AbstractFile] = + fs flatMap (s => Option(AbstractFile getFile (pre + s))) toSet private def copyTestFiles(testDir: File, destDir: File) { - val filter = - new FilenameFilter { - def accept(dir: File, name: String) = - (name.endsWith(".scala") || name.endsWith(".java")) && - ((new File(dir, name).isFile)) - } - (testDir.listFiles(filter)).foreach( f => - fileManager.copyFile(f, new File(destDir, f.getName)) ) + testDir.listFiles.toList filter (f => isJavaOrScala(f) && f.isFile) foreach { f => + fileManager.copyFile(f, new File(destDir, f.getName)) + } } def showLog(logFile: File) { diff --git a/src/scalap/scala/tools/scalap/Main.scala b/src/scalap/scala/tools/scalap/Main.scala index 90f1963d14..9534b2f84e 100644 --- a/src/scalap/scala/tools/scalap/Main.scala +++ b/src/scalap/scala/tools/scalap/Main.scala @@ -13,6 +13,7 @@ import java.io.{File, PrintStream, OutputStreamWriter, ByteArrayOutputStream} import scalax.rules.scalasig._ import tools.nsc.io.AbstractFile import tools.nsc.util.{ ClassPath, JavaClassPath } +import tools.util.PathResolver import ClassPath.DefaultJavaContext /**The main object used to execute scalap on the command-line. @@ -264,13 +265,8 @@ object Main { verbose = arguments contains "-verbose" printPrivates = arguments contains "-private" // construct a custom class path - val path = arguments.getArgument("-classpath") match { - case None => arguments.getArgument("-cp") match { - case None => EmptyClasspath - case Some(path) => new JavaClassPath("", "", path, "", "", DefaultJavaContext) - } - case Some(path) => new JavaClassPath("", "", path, "", "", DefaultJavaContext) - } + def cparg = List("-classpath", "-cp") map (arguments getArgument _) reduceLeft (_ orElse _) + val path = cparg map (PathResolver fromPathString _) getOrElse EmptyClasspath // print the classpath if output is verbose if (verbose) { Console.println(Console.BOLD + "CLASSPATH" + Console.RESET + " = " + path) |