diff options
author | Paul Phillips <paulp@improving.org> | 2009-05-26 18:12:53 +0000 |
---|---|---|
committer | Paul Phillips <paulp@improving.org> | 2009-05-26 18:12:53 +0000 |
commit | 103c97f7deef02d81d6d87c21f751899c63683b1 (patch) | |
tree | 50680eb96e39e016f28df03582164cce1b73d5e2 /src/compiler/scala/tools/ant/Scalac.scala | |
parent | 5e12bab4777dc63711834cd39bf8514fb7e8da40 (diff) | |
download | scala-103c97f7deef02d81d6d87c21f751899c63683b1.tar.gz scala-103c97f7deef02d81d6d87c21f751899c63683b1.tar.bz2 scala-103c97f7deef02d81d6d87c21f751899c63683b1.zip |
A big yet interim patch emerging from my attemp...
A big yet interim patch emerging from my attempts to centralize common
classloader-related code. As it turns out, not that much of the
patch is directly associated with that. Most of it is cleanup in the
neighborhoods I was visiting, but there are a few new library files
about which I'm open to feedback:
scala/util/control/Exception - lots of exception handling code.
scala/net/Utility - what would be the first file in scala.net.*,
more code to follow if that sounds like a good package idea.
scala/util/ScalaClassLoader - mostly convenience methods right
now, more sophistication to come
Also, this adds a :jar command to the repl which adds a jar to your
classpath and replays the session.
Diffstat (limited to 'src/compiler/scala/tools/ant/Scalac.scala')
-rw-r--r-- | src/compiler/scala/tools/ant/Scalac.scala | 310 |
1 files changed, 123 insertions, 187 deletions
diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala index e9007c4238..3ede08f40c 100644 --- a/src/compiler/scala/tools/ant/Scalac.scala +++ b/src/compiler/scala/tools/ant/Scalac.scala @@ -12,7 +12,7 @@ package scala.tools.ant import java.io.{File,PrintWriter,BufferedWriter,FileWriter} -import org.apache.tools.ant.{BuildException, Project} +import org.apache.tools.ant.{ BuildException, Project, AntClassLoader } import org.apache.tools.ant.taskdefs.{MatchingTask,Java} import org.apache.tools.ant.types.{Path, Reference, FileSet} import org.apache.tools.ant.util.{FileUtils, GlobPatternMapper, @@ -106,6 +106,10 @@ class Scalac extends MatchingTask { * <code>unchecked</code> properties. */ object Flag extends PermissibleValue { val values = List("yes", "no", "on", "off") + def toBoolean(flag: String) = + if (flag == "yes" || flag == "on") Some(true) + else if (flag == "no" || flag == "off") Some(false) + else None } /** The directories that contain source files to compile. */ @@ -164,23 +168,39 @@ class Scalac extends MatchingTask { * (not only the number of files). */ protected var scalacDebugging: Boolean = false + /** Helpers */ + private def setOrAppend(old: Option[Path], arg: Path): Option[Path] = old match { + case Some(x) => x append arg ; Some(x) + case None => Some(arg) + } + private def pathAsList(p: Option[Path], name: String): List[File] = p match { + case None => error("Member '" + name + "' is empty.") + case Some(x) => x.list.toList map nameToFile + } + private def createNewPath(getter: () => Option[Path], setter: (Option[Path]) => Unit) = { + if (getter().isEmpty) + setter(Some(new Path(getProject()))) + + getter().get.createPath() + } + + private def plural(xs: List[Any]) = if (xs.size > 1) "s" else "" + private def plural(x: Int) = if (x > 1) "s" else "" + /*============================================================================*\ ** Properties setters ** \*============================================================================*/ + /** Sets the srcdir attribute. Used by Ant. * @param input The value of <code>origin</code>. */ def setSrcdir(input: Path) { - if (origin.isEmpty) origin = Some(input) - else origin.get.append(input) + origin = setOrAppend(origin, input) } /** Sets the <code>origin</code> as a nested src Ant parameter. * @return An origin path to be configured. */ - def createSrc(): Path = { - if (origin.isEmpty) origin = Some(new Path(getProject())) - origin.get.createPath() - } + def createSrc(): Path = createNewPath(origin _, origin = _) /** Sets the <code>origin</code> as an external reference Ant parameter. * @param input A reference to an origin path. */ @@ -194,20 +214,16 @@ class Scalac extends MatchingTask { /** Sets the <code>classpath</code> attribute. Used by Ant. * @param input The value of <code>classpath</code>. */ def setClasspath(input: Path) { - if (classpath.isEmpty) classpath = Some(input) - else classpath.get.append(input) + classpath = setOrAppend(classpath, input) } /** Sets the <code>compilerPath</code> attribute. Used by Ant. * @param input The value of <code>compilerPath</code>. */ def setCompilerPath(input : Path) { - if(compilerPath.isEmpty) compilerPath = Some(input) - else compilerPath.get.append(input) + compilerPath = setOrAppend(compilerPath, input) } - def createCompilerPath: Path = { - if (compilerPath.isEmpty) compilerPath = Some(new Path(getProject())) - compilerPath.get.createPath() - } + def createCompilerPath: Path = createNewPath(compilerPath _, compilerPath = _) + /** Sets the <code>compilerpathref</code> attribute. Used by Ant. * @param input The value of <code>compilerpathref</code>. */ def setCompilerPathRef(input: Reference) { @@ -216,10 +232,7 @@ class Scalac extends MatchingTask { /** Sets the <code>classpath</code> as a nested classpath Ant parameter. * @return A class path to be configured. */ - def createClasspath(): Path = { - if (classpath.isEmpty) classpath = Some(new Path(getProject())) - classpath.get.createPath() - } + def createClasspath(): Path = createNewPath(classpath _, classpath = _) /** Sets the <code>classpath</code> as an external reference Ant parameter. * @param input A reference to a class path. */ @@ -230,16 +243,12 @@ class Scalac extends MatchingTask { /** Sets the <code>sourcepath</code> attribute. Used by Ant. * @param input The value of <code>sourcepath</code>. */ def setSourcepath(input: Path) { - if (sourcepath.isEmpty) sourcepath = Some(input) - else sourcepath.get.append(input) + sourcepath = setOrAppend(sourcepath, input) } /** Sets the <code>sourcepath</code> as a nested sourcepath Ant parameter. * @return A source path to be configured. */ - def createSourcepath(): Path = { - if (sourcepath.isEmpty) sourcepath = Some(new Path(getProject())) - sourcepath.get.createPath() - } + def createSourcepath(): Path = createNewPath(sourcepath _, sourcepath = _) /** Sets the <code>sourcepath</code> as an external reference Ant parameter. * @param input A reference to a source path. */ @@ -251,17 +260,13 @@ class Scalac extends MatchingTask { * * @param input The value of <code>bootclasspath</code>. */ def setBootclasspath(input: Path) { - if (bootclasspath.isEmpty) bootclasspath = Some(input) - else bootclasspath.get.append(input) + bootclasspath = setOrAppend(bootclasspath, input) } /** Sets the <code>bootclasspath</code> as a nested sourcepath Ant * parameter. * @return A source path to be configured. */ - def createBootclasspath(): Path = { - if (bootclasspath.isEmpty) bootclasspath = Some(new Path(getProject())) - bootclasspath.get.createPath() - } + def createBootclasspath(): Path = createNewPath(bootclasspath _, bootclasspath = _) /** Sets the <code>bootclasspath</code> as an external reference Ant * parameter. @@ -272,15 +277,11 @@ class Scalac extends MatchingTask { /** Sets the external extensions path attribute. Used by Ant. * @param input The value of <code>extdirs</code>. */ def setExtdirs(input: Path) = - if (extdirs.isEmpty) extdirs = Some(input) - else extdirs.get.append(input) + extdirs = setOrAppend(extdirs, input) /** Sets the <code>extdirs</code> as a nested sourcepath Ant parameter. * @return An extensions path to be configured. */ - def createExtdirs(): Path = { - if (extdirs.isEmpty) extdirs = Some(new Path(getProject())) - extdirs.get.createPath() - } + def createExtdirs(): Path = createNewPath(extdirs _, extdirs = _) /** Sets the <code>extdirs</code> as an external reference Ant parameter. * @param input A reference to an extensions path. */ @@ -345,28 +346,19 @@ class Scalac extends MatchingTask { /** Set the <code>deprecation</code> info attribute. * @param input One of the flags <code>yes/no</code> or <code>on/off</code>. */ def setDeprecation(input: String) { - if (Flag.isPermissible(input)) - deprecation = Some("yes" == input || "on" == input) - else - error("Unknown deprecation flag '" + input + "'") + deprecation = Flag toBoolean input orElse error("Unknown deprecation flag '" + input + "'") } /** Set the <code>optimise</code> info attribute. * @param input One of the flags <code>yes/no</code> or <code>on/off</code>. */ def setOptimise(input: String) { - if (Flag.isPermissible(input)) - optimise = Some("yes" == input || "on" == input) - else - error("Unknown optimisation flag '" + input + "'") + optimise = Flag toBoolean input orElse error("Unknown optimisation flag '" + input + "'") } /** Set the <code>unchecked</code> info attribute. * @param input One of the flags <code>yes/no</code> or <code>on/off</code>. */ def setUnchecked(input: String) { - if (Flag.isPermissible(input)) - unchecked = Some("yes" == input || "on" == input) - else - error("Unknown unchecked flag '" + input + "'") + unchecked = Flag toBoolean input orElse error("Unknown unchecked flag '" + input + "'") } /** Sets the <code>force</code> attribute. Used by Ant. @@ -380,7 +372,6 @@ class Scalac extends MatchingTask { def setScalacdebugging(input: Boolean) { scalacDebugging = input } def setAssemname(input: String) { assemname = Some(input) } - def setAssemrefs(input: String) { assemrefs = Some(input) } /*============================================================================*\ @@ -390,16 +381,12 @@ class Scalac extends MatchingTask { /** Gets the value of the <code>classpath</code> attribute in a * Scala-friendly form. * @return The class path as a list of files. */ - protected def getClasspath: List[File] = - if (classpath.isEmpty) error("Member 'classpath' is empty.") - else List.fromArray(classpath.get.list()).map(nameToFile) + protected def getClasspath: List[File] = pathAsList(classpath, "classpath") /** Gets the value of the <code>origin</code> attribute in a * Scala-friendly form. * @return The origin path as a list of files. */ - protected def getOrigin: List[File] = - if (origin.isEmpty) error("Member 'origin' is empty.") - else List.fromArray(origin.get.list()).map(nameToFile) + protected def getOrigin: List[File] = pathAsList(origin, "origin") /** Gets the value of the <code>destination</code> attribute in a * Scala-friendly form. @@ -411,33 +398,22 @@ class Scalac extends MatchingTask { /** Gets the value of the <code>sourcepath</code> attribute in a * Scala-friendly form. * @return The source path as a list of files. */ - protected def getSourcepath: List[File] = - if (sourcepath.isEmpty) error("Member 'sourcepath' is empty.") - else List.fromArray(sourcepath.get.list()).map(nameToFile) + protected def getSourcepath: List[File] = pathAsList(sourcepath, "sourcepath") /** Gets the value of the <code>bootclasspath</code> attribute in a * Scala-friendly form. * @return The boot class path as a list of files. */ - protected def getBootclasspath: List[File] = - if (bootclasspath.isEmpty) error("Member 'bootclasspath' is empty.") - else List.fromArray(bootclasspath.get.list()).map(nameToFile) + protected def getBootclasspath: List[File] = pathAsList(bootclasspath, "bootclasspath") /** Gets the value of the <code>extdirs</code> attribute in a * Scala-friendly form. * @return The extensions path as a list of files. */ - protected def getExtdirs: List[File] = - if (extdirs.isEmpty) error("Member 'extdirs' is empty.") - else List.fromArray(extdirs.get.list()).map(nameToFile) + protected def getExtdirs: List[File] = pathAsList(extdirs, "extdirs") /*============================================================================*\ ** Compilation and support methods ** \*============================================================================*/ - /** This is forwarding method to circumvent bug #281 in Scala 2. Remove when - * bug has been corrected. */ - override protected def getDirectoryScanner(baseDir: File) = - super.getDirectoryScanner(baseDir) - /** Transforms a string name into a file relative to the provided base * directory. * @param base A file pointing to the location relative to which the name @@ -503,59 +479,52 @@ class Scalac extends MatchingTask { protected def initialize: (Settings, List[File], Boolean) = { // Tests if all mandatory attributes are set and valid. if (origin.isEmpty) error("Attribute 'srcdir' is not set.") - if (getOrigin.isEmpty) error("Attribute 'srcdir' is not set.") if (!destination.isEmpty && !destination.get.isDirectory()) error("Attribute 'destdir' does not refer to an existing directory.") if (destination.isEmpty) destination = Some(getOrigin.head) val mapper = new GlobPatternMapper() - mapper.setTo("*.class") - mapper.setFrom("*.scala") + mapper setTo "*.class" + mapper setFrom "*.scala" var javaOnly = true + def getOriginFiles(originDir: File) = { + val includedFiles = getDirectoryScanner(originDir).getIncludedFiles() + val javaFiles = includedFiles filter (_ endsWith ".java") + val scalaFiles = { + val xs = includedFiles filter (_ endsWith ".scala") + if (force) xs + else new SourceFileScanner(this).restrict(xs, originDir, destination.get, mapper) + } + + javaOnly = javaOnly && (scalaFiles.length == 0) + val list = (scalaFiles ++ javaFiles).toList + + if (scalacDebugging && !list.isEmpty) + log("Compiling source file%s: %s to %s".format( + plural(list), + list.mkString(", "), + getDestination.toString + )) + else if (!list.isEmpty) { + val str = + if (javaFiles.isEmpty) "%d source file%s".format(list.length, plural(list)) + else "%d scala and %d java source files".format(scalaFiles.length, javaFiles.length) + log("Compiling %s to %s".format(str, getDestination.toString)) + } + else log("No files selected for compilation", Project.MSG_VERBOSE) + + list + } + // Scans source directories to build up a compile lists. // If force is false, only files were the .class file in destination is // older than the .scala file will be used. val sourceFiles: List[File] = - for { - val originDir <- getOrigin - val originFiles <- { - val includedFiles = getDirectoryScanner(originDir).getIncludedFiles() - var scalaFiles = includedFiles.filter(_.endsWith(".scala")) - val javaFiles = includedFiles.filter(_.endsWith(".java")) - if (!force) { - scalaFiles = new SourceFileScanner(this). - restrict(scalaFiles, originDir, destination.get, mapper) - } - javaOnly = javaOnly && (scalaFiles.length == 0) - val list = scalaFiles.toList ::: javaFiles.toList - if (scalacDebugging && list.length > 0) - log( - list.mkString( - "Compiling source file" + - (if (list.length > 1) "s: " else ": "), - ", ", - " " - ) + "to " + getDestination.toString - ) - else if (list.length > 0) - log( - "Compiling " + ( - if (javaFiles.length > 0) - (scalaFiles.length +" scala and "+ javaFiles.length +" java source files") - else - (list.length +" source file"+ (if (list.length > 1) "s" else "")) - ) +" to "+ getDestination.toString - ) - else - log("No files selected for compilation", Project.MSG_VERBOSE) - list - } - } - yield { - log(originFiles, Project.MSG_DEBUG) - nameToFile(originDir)(originFiles) + for (originDir <- getOrigin ; originFile <- getOriginFiles(originDir)) yield { + log(originFile, Project.MSG_DEBUG) + nameToFile(originDir)(originFile) } // Builds-up the compilation settings for Scalac with the existing Ant @@ -597,115 +566,82 @@ class Scalac extends MatchingTask { override def execute() { val (settings, sourceFiles, javaOnly) = initialize - if (sourceFiles.isEmpty || javaOnly) { + if (sourceFiles.isEmpty || javaOnly) return - } - if(fork) { - //TODO - Error - executeFork(settings, sourceFiles) - } else { - executeInternal(settings, sourceFiles) - } + + if (fork) executeFork(settings, sourceFiles) // TODO - Error + else executeInternal(settings, sourceFiles) } - protected def executeFork(settings: Settings, sourceFiles : List[File]) { - val java = new Java(this) // set this as owner - java.setFork(true) - // using 'setLine' creates multiple arguments out of a space-separated string - for(args <- jvmArgs) { - java.createJvmarg().setLine(args) - } + protected def executeFork(settings: Settings, sourceFiles: List[File]) { + val java = new Java(this) + java setFork true + // using 'setLine' creates multiple arguments out of a space-separated string + jvmArgs foreach { java.createJvmarg() setLine _ } - //Determine the path for scalac! - val scalacPath = { + // use user-provided path or retrieve from classloader + // TODO - Allow user to override the compiler classpath + val scalacPath: Path = { val path = new Path(getProject) - compilerPath match { - case Some(p) => - //Use the user provided path - path.add(p) - case None => - //Pull classpath off our classloader - //TODO - Allow user to override the compiler classpath - import _root_.org.apache.tools.ant.AntClassLoader - val classLoader = getClass.getClassLoader - if(classLoader.isInstanceOf[AntClassLoader]) { - path.add(new Path(getProject, classLoader.asInstanceOf[AntClassLoader].getClasspath)) - } else { - throw new BuildException("Cannot determine default classpath for sclac, please specify one!") - } + if (compilerPath.isDefined) path add compilerPath.get + else getClass.getClassLoader match { + case cl: AntClassLoader => path add new Path(getProject, cl.getClasspath) + case _ => error("Cannot determine default classpath for sclac, please specify one!") } path } - java.setClasspath(scalacPath) - - java.setClassname("scala.tools.nsc.Main") - //if (!timeout.isEmpty) java.setTimeout(timeout.get) + java setClasspath scalacPath + java setClassname "scala.tools.nsc.Main" - //Write all settings to a temporary file + // Write all settings to a temporary file def writeSettings() : File = { def escapeArgument(arg : String) = if(arg.matches(".*\\s.*")) ('"' + arg + '"') else arg val file = File.createTempFile("scalac-ant-",".args") file.deleteOnExit() val out = new PrintWriter(new BufferedWriter(new FileWriter(file))) + try { - for ( setting <- settings.allSettings; - arg <- setting.unparse){ - out.println(escapeArgument(arg)) - } - for (file <- sourceFiles) { - out.println(file.getAbsolutePath) - } - } finally { - out.close(); + for (setting <- settings.allSettings ; arg <- setting.unparse) + out println escapeArgument(arg) + for (file <- sourceFiles) + out println file.getAbsolutePath } + finally out.close() + file } - java.createArg().setValue("@" + writeSettings.getCanonicalPath) - log(java.getCommandLine.getCommandline.mkString("", " ", ""), Project.MSG_VERBOSE) + java.createArg() setValue ("@" + writeSettings.getCanonicalPath) + log(java.getCommandLine.getCommandline.mkString(" "), Project.MSG_VERBOSE) + val res = java.executeJava() if (failonerror && res != 0) error("Compilation failed because of an internal compiler error;"+ " see the error output for details.") - } + /** Performs the compilation. */ protected def executeInternal(settings: Settings, sourceFiles : List[File]) { - - val reporter = new ConsoleReporter(settings) - - // Compiles the actual code - val compiler = newGlobal(settings, reporter) - try { - (new compiler.Run).compile(sourceFiles.map (_.toString)) - } - catch { - case exception: Throwable if (exception.getMessage ne null) => - exception.printStackTrace() - error("Compile failed because of an internal compiler error (" + - exception.getMessage + "); see the error output for details.") - case exception => - exception.printStackTrace() - error("Compile failed because of an internal compiler error " + - "(no error message provided); see the error output for details.") + val compiler = newGlobal(settings, reporter) // compiles the actual code + + try new compiler.Run compile (sourceFiles map (_.toString)) + catch { + case ex: Throwable => + ex.printStackTrace() + val msg = if (ex.getMessage == null) "no error message provided" else ex.getMessage + error("Compile failed because of an internal compiler error (" + msg + "); see the error output for details.") } + reporter.printSummary() if (reporter.hasErrors) { - val msg = - "Compile failed with " + - reporter.ERROR.count + " error" + - (if (reporter.ERROR.count > 1) "s" else "") + - "; see the compiler error output for details." + val msg = "Compile failed with %d error%s; see the compiler error output for details.".format( + reporter.ERROR.count, plural(reporter.ERROR.count)) if (failonerror) error(msg) else log(msg) } else if (reporter.WARNING.count > 0) - log( - "Compile suceeded with " + - reporter.WARNING.count + " warning" + - (if (reporter.WARNING.count > 1) "s" else "") + - "; see the compiler output for details.") + log("Compile succeeded with %d warning%s; see the compiler output for details.".format( + reporter.WARNING.count, plural(reporter.WARNING.count))) } - } |