From eb94d965c95024670e48f96abcb75394caf4e083 Mon Sep 17 00:00:00 2001 From: Gilles Dubochet Date: Fri, 23 Dec 2005 18:17:24 +0000 Subject: 1. 2. Modified the build script to use the new optional Ant tasks that have been added to 'lib/', which is much cleaner. --- build.xml | 267 +++++++++--------- lib/ant-contrib.jar.desired.sha1 | 1 + src/compiler/scala/tools/ant/Scalac.scala | 454 ++++++++++++++++++++++++++++++ 3 files changed, 595 insertions(+), 127 deletions(-) create mode 100644 lib/ant-contrib.jar.desired.sha1 create mode 100644 src/compiler/scala/tools/ant/Scalac.scala diff --git a/build.xml b/build.xml index eaa66d2e99..a583b43625 100644 --- a/build.xml +++ b/build.xml @@ -10,11 +10,9 @@ Running Sabbus for Scala-core - + @@ -24,9 +22,6 @@ Sabbus requires additional memory. Please set the 'ANT_OPTS' environment property to '-Xmx256M' or more. - - - @@ -42,7 +37,7 @@ - + @@ -54,6 +49,7 @@ + @@ -81,26 +77,11 @@ - - - - - - - - - - + - - - - - + starr.lib.jar=${starr.lib.jar} @@ -141,6 +122,35 @@ ant.jar=${ant.jar} + ant-contrib.jar=${ant-contrib.jar} + + + + + + + + + + + + + + + + + + + + + + + @@ -154,28 +164,47 @@ - - - - - - - - - - + + + + + + + + + + + + + + + + + + + + + + + Sabbus detected a Cygwin environnement + + Sabbus detected a Windows environnement + + + Sabbus detected a UNIX environnement + + + System environment could not be determined + + - - - @@ -185,7 +214,7 @@ @@ -196,17 +225,15 @@ + + - - - + @@ -271,18 +298,17 @@ - + + + scalahack="yes"> @@ -290,8 +316,7 @@ + usepredefs="no"> @@ -299,8 +324,7 @@ + destdir="${locker.lib.dir}"> @@ -313,7 +337,7 @@ + scalahack="yes"> @@ -321,8 +345,7 @@ + destdir="${locker.comp.dir}"> @@ -333,7 +356,7 @@ - - - + + + scalahack="yes"> @@ -364,8 +385,7 @@ + usepredefs="no"> @@ -373,8 +393,7 @@ + destdir="${quick.lib.dir}"> @@ -387,7 +406,7 @@ + scalahack="yes"> @@ -395,8 +414,7 @@ + destdir="${quick.comp.dir}"> @@ -407,7 +425,7 @@ - - + @@ -431,7 +447,7 @@ + scalahack="yes"> @@ -439,8 +455,7 @@ + usepredefs="no"> @@ -448,8 +463,7 @@ + destdir="${strap.lib.dir}"> @@ -462,7 +476,7 @@ + scalahack="yes"> @@ -470,8 +484,7 @@ + destdir="${strap.comp.dir}"> @@ -482,7 +495,7 @@ - - + @@ -508,7 +521,7 @@ - + @@ -519,45 +532,39 @@ Build is stable - + Docs is not available yet. - + - - - - - - + + + + + + - + + + + + - - + + + includeemptydirs="yes" + quiet="yes" + failonerror="no"/> diff --git a/lib/ant-contrib.jar.desired.sha1 b/lib/ant-contrib.jar.desired.sha1 new file mode 100644 index 0000000000..05723e152e --- /dev/null +++ b/lib/ant-contrib.jar.desired.sha1 @@ -0,0 +1 @@ +6f02b1db445a31020e3fe0a137bc819595dbc387 ?ant-contrib.jar diff --git a/src/compiler/scala/tools/ant/Scalac.scala b/src/compiler/scala/tools/ant/Scalac.scala new file mode 100644 index 0000000000..6967e7d794 --- /dev/null +++ b/src/compiler/scala/tools/ant/Scalac.scala @@ -0,0 +1,454 @@ +/* __ ______________ *\ +** / |/ / ____/ ____/ ** +** / | | /___ / /___ ** +** /_/|__/_____/_____/ Copyright 2005-2006 LAMP/EPFL ** +** +** $Id$ +\* */ + +package scala.tools.ant { + + import java.io.File + import java.net.{URL, URLClassLoader} + import java.util.{ArrayList, Vector} + + import org.apache.tools.ant.{AntClassLoader, BuildException, + DirectoryScanner, Project} + import org.apache.tools.ant.taskdefs.MatchingTask + import org.apache.tools.ant.types.Path + import org.apache.tools.ant.util.{FileUtils, GlobPatternMapper, + SourceFileScanner} + import org.apache.tools.ant.types.{EnumeratedAttribute, Reference} + + import scala.tools.nsc.reporters.{Reporter, ConsoleReporter} + import scala.tools.nsc.{Global, FatalError, Settings} + + /** An Ant task to compile with the new Scala compiler (NSC). + * This task can take the following parameters as attributes:
    + *
  • srcdir (mandatory),
  • + *
  • srcref,
  • + *
  • destdir,
  • + *
  • classpath,
  • + *
  • classpathref,
  • + *
  • sourcepath,
  • + *
  • sourcepathref,
  • + *
  • bootclasspath,
  • + *
  • bootclasspathref,
  • + *
  • extdirs,
  • + *
  • extdirsref,
  • + *
  • encoding,
  • + *
  • force,
  • + *
  • logging,
  • + *
  • logphase,
  • + *
  • usepredefs,
  • + *
  • debugcode.
  • + *
+ * It also takes the following parameters as nested elements:
    + *
  • src (for srcdir),
  • + *
  • classpath,
  • + *
  • sourcepath,
  • + *
  • bootclasspath,
  • + *
  • extdirs.
  • + *
+ * + * @author Gilles Dubochet */ + class Scalac extends MatchingTask { + + private val SCALA_PRODUCT: String = + System.getProperty("scala.product", "scalac") + private val SCALA_VERSION: String = + System.getProperty("scala.version", "Unknown version") + + /** The unique Ant file utilities instance to use in this task. */ + private val fileUtils = FileUtils.newFileUtils() + +/******************************************************************************\ +** Ant user-properties ** +\******************************************************************************/ + + abstract class PermissibleValue { + val values: List[String] + def isPermissible(value: String): Boolean = + (value == "") || values.exists(.startsWith(value)) + } + + /** Defines valid values for the logging property. */ + object LoggingLevel extends PermissibleValue { + val values = List("none", "verbose", "debug") + } + + /** Defines valid values for properties that refer to compiler phases. */ + object CompilerPhase extends PermissibleValue { + val values = List("namer", "typer", "pickler", "uncurry", "tailcalls", + "transmatch", "explicitouter", "erasure", "lambdalift", + "flatten", "constructors", "mixin", "icode", "jvm", + "terminal") + } + + /** The directories that contain source files to compile. */ + private var origin: Option[Path] = None + /** The directory to put the compiled files in. */ + private var destination: Option[File] = None + + /** The class path to use for this compilation. */ + private var classpath: Option[Path] = None + /** The source path to use for this compilation. */ + private var sourcepath: Option[Path] = None + /** The boot class path to use for this compilation. */ + private var bootclasspath: Option[Path] = None + /** The external extensions path to use for this compilation. */ + private var extdirs: Option[Path] = None + + /** The character encoding of the files to compile. */ + private var encoding: Option[String] = None + + /** Whether to force compilation of all files or not. */ + private var force: Boolean = false + /** How much logging output to print. Either none (default), + * verbose or debug. */ + private var logging: Option[String] = None + /** Which compilation phases should be logged during compilation. */ + private var logPhase: List[String] = Nil + + /** Whether to use implicit predefined values or not. */ + private var usepredefs: Boolean = true; + /** Instruct the compiler to generate debugging information */ + private var debugCode: Boolean = false + +/******************************************************************************\ +** Properties setters ** +\******************************************************************************/ + + /** Sets the srcdir attribute. Used by Ant. + * @param input The value of origin. */ + def setSrcdir(input: Path) = + if (origin.isEmpty) origin = Some(input) + else origin.get.append(input) + + /** Sets the origin 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() + } + + /** Sets the origin as an external reference Ant parameter. + * @param input A reference to an origin path. */ + def setSrcref(input: Reference) = + createSrc().setRefid(input) + + /** Sets the destdir attribute. Used by Ant. + * @param input The value of destination. */ + def setDestdir(input: File) = + destination = Some(input) + + /** Sets the classpath attribute. Used by Ant. + * @param input The value of classpath. */ + def setClasspath(input: Path) = + if (classpath.isEmpty) classpath = Some(input) + else classpath.get.append(input) + + /** Sets the classpath 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() + } + + /** Sets the classpath as an external reference Ant parameter. + * @param input A reference to a class path. */ + def setClasspathref(input: Reference) = + createClasspath().setRefid(input) + + /** Sets the sourcepath attribute. Used by Ant. + * @param input The value of sourcepath. */ + def setSourcepath(input: Path) = + if (sourcepath.isEmpty) sourcepath = Some(input) + else sourcepath.get.append(input) + + /** Sets the sourcepath 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() + } + + /** Sets the sourcepath as an external reference Ant parameter. + * @param input A reference to a source path. */ + def setSourcepathref(input: Reference) = + createSourcepath().setRefid(input) + + /** Sets the boot classpath attribute. Used by Ant. + * @param input The value of bootclasspath. */ + def setBootclasspath(input: Path) = + if (bootclasspath.isEmpty) bootclasspath = Some(input) + else bootclasspath.get.append(input) + + /** Sets the bootclasspath 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() + } + + /** Sets the bootclasspath as an external reference Ant + * parameter. + * @param input A reference to a source path. */ + def setBootclasspathref(input: Reference) = + createBootclasspath().setRefid(input) + + /** Sets the external extensions path attribute. Used by Ant. + * @param input The value of extdirs. */ + def setExtdirs(input: Path) = + if (extdirs.isEmpty) extdirs = Some(input) + else extdirs.get.append(input) + + /** Sets the extdirs 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() + } + + /** Sets the extdirs as an external reference Ant parameter. + * @param input A reference to an extensions path. */ + def setExtdirsref(input: Reference) = + createExtdirs().setRefid(input) + + /** Sets the encoding attribute. Used by Ant. + * @param input The value of encoding. */ + def setEncoding(input: String): Unit = + encoding = Some(input) + + /** Sets the force attribute. Used by Ant. + * @param input The value for force. */ + def setForce(input: Boolean): Unit = + force = input + + /** Sets the logging level attribute. Used by Ant. + * @param input The value for logging. */ + def setLogging(input: String) = + if (LoggingLevel.isPermissible(input)) logging = Some(input) + else error("Logging level '" + input + "' does not exist.") + + /** Sets the log attribute. Used by Ant. + * @param input The value for logPhase. */ + def setLogPhase(input: String) = { + logPhase = List.fromArray(input.split(",")).flatMap(s: String => { + val st = s.trim() + if (CompilerPhase.isPermissible(st)) + (if (input != "") List(st) else Nil) + else { + error("Phase " + st + " in log does not exist.") + Nil + } + }) + } + + /** Sets the use predefs attribute. Used by Ant. + * @param input The value for usepredefs. */ + def setUsepredefs(input: Boolean): Unit = + usepredefs = input; + + /** Set the debug info attribute. */ + def setDebugCode(input: Boolean): Unit = + debugCode = input + +/******************************************************************************\ +** Properties getters ** +\******************************************************************************/ + + /** Gets the value of the classpath attribute in a Scala-friendly form. + * @returns The class path as a list of files. */ + private def getClasspath: List[File] = + if (classpath.isEmpty) error("Member 'classpath' is empty.") + else + List.fromArray(classpath.get.list()).map(nameToFile) + + /** Gets the value of the origin attribute in a Scala-friendly form. + * @returns The origin path as a list of files. */ + private def getOrigin: List[File] = + if (origin.isEmpty) error("Member 'origin' is empty.") + else List.fromArray(origin.get.list()).map(nameToFile) + + /** Gets the value of the destination attribute in a Scala-friendly form. + * @returns The destination as a file. */ + private def getDestination: File = + if (destination.isEmpty) error("Member 'destination' is empty.") + else existing(getProject().resolveFile(destination.get.toString())) + + /** Gets the value of the sourcepath attribute in a Scala-friendly form. + * @returns The source path as a list of files. */ + private def getSourcepath: List[File] = + if (sourcepath.isEmpty) error("Member 'sourcepath' is empty.") + else + List.fromArray(sourcepath.get.list()).map(nameToFile) + + /** Gets the value of the bootclasspath attribute in a Scala-friendly form. + * @returns The boot class path as a list of files. */ + private def getBootclasspath: List[File] = + if (bootclasspath.isEmpty) error("Member 'bootclasspath' is empty.") + else + List.fromArray(bootclasspath.get.list()).map(nameToFile) + + /** Gets the value of the extdirs attribute in a Scala-friendly form. + * @returns The extensions path as a list of files. */ + private def getExtdirs: List[File] = + if (extdirs.isEmpty) error("Member 'extdirs' is empty.") + else List.fromArray(extdirs.get.list()).map(nameToFile) + +/******************************************************************************\ +** 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: java.io.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 + * will be resolved. + * @param name A relative or absolute path to the file as a string. + * @return A file created from the name and the base file. */ + private def nameToFile(base: File)(name: String): File = + existing(fileUtils.resolveFile(base, name)) + + /** Transforms a string name into a file relative to the build root + * directory. + * @param name A relative or absolute path to the file as a string. + * @return A file created from the name. */ + private def nameToFile(name: String): File = + existing(getProject().resolveFile(name)) + + /** Tests if a file exists and prints a warning in case it doesn't. Always + * returns the file, even if it doesn't exist. + * @param file A file to test for existance. + * @return The same file. */ + private def existing(file: File): File = { + if (!file.exists()) + log("Element '" + file.toString() + "' does not exist.", + Project.MSG_WARN) + file + } + + /** Transforms a path into a Scalac-readable string. + * @param path A path to convert. + * @return A string-representation of the path like 'a.jar:b.jar'. */ + private def asString(path: List[File]): String = + path.map(asString).mkString("", File.pathSeparator, "") + + /** Transforms a file into a Scalac-readable string. + * @param path A file to convert. + * @return A string-representation of the file like '/x/k/a.scala'. */ + private def asString(file: File): String = + file.getAbsolutePath() + + /** Generates a build error. Error location will be the current task in the + * ant file. + * @param message A message describing the error. + * @throws BuildException A build error exception thrown in every case. */ + private def error(message: String): All = + throw new BuildException(message, getLocation()) + +/******************************************************************************\ +** The big execute method ** +\******************************************************************************/ + + /** Performs the compilation. */ + override def execute() = { + + // 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") + + // 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 originFile <- { + var includedFiles = + getDirectoryScanner(originDir).getIncludedFiles() + if (!force) { + includedFiles = new SourceFileScanner(this). + restrict(includedFiles, originDir, destination.get, mapper) + } + (List.fromArray(includedFiles)). + map(nameToFile(originDir)) + }) + yield { + log(originFile.toString(), Project.MSG_DEBUG) + originFile + } + + if (sourceFiles.length == 0) log("No files selected for compilation") + else log("Compiling " + sourceFiles.length + " source file" + + (if (sourceFiles.length > 1) "s" else "") + + (" to " + getDestination.toString())) + + // Builds-up the compilation settings for Scalac with the existing Ant + // parameters. + val reporter = new ConsoleReporter() + val settings = new Settings(error) + settings.outdir.value = asString(destination.get) + if (!classpath.isEmpty) + settings.classpath.value = asString(getClasspath) + if (!sourcepath.isEmpty) + settings.sourcepath.value = asString(getSourcepath) + else if (origin.get.size() > 0) + settings.sourcepath.value = origin.get.list()(0) + if (!bootclasspath.isEmpty) + settings.bootclasspath.value = asString(getBootclasspath) + if (!extdirs.isEmpty) settings.extdirs.value = asString(getExtdirs) + if (!encoding.isEmpty) settings.encoding.value = encoding.get + if (!logging.isEmpty && logging.get == "verbose") + settings.verbose.value = true + else if (!logging.isEmpty && logging.get == "debug") { + settings.verbose.value = true + settings.debug.value = true + } + if (!logPhase.isEmpty) settings.log.value = logPhase + settings.nopredefs.value = !usepredefs; + settings.debuginfo.value = debugCode + + // Compiles the actual code + val compiler = new Global(settings, reporter) + try { + (new compiler.Run).compile(sourceFiles.map(f:File=>f.toString())) + if (reporter.errors > 0) + error("Compile failed with " + + reporter.errors + " error" + + (if (reporter.errors > 1) "s" else "") + + "; see the compiler error output for details." + ) + } + catch { + case exception @ FatalError(msg) => { + exception.printStackTrace() + if (settings.debug.value) exception.printStackTrace() + error("Compile failed because of an internal compiler error (" + msg + + "); see the error output for details.") + } + } + if (reporter.warnings > 0) + log("Compile suceeded with " + + reporter.errors + " warning" + + (if (reporter.warnings > 1) "s" else "") + + "; see the compiler output for details." + ) + reporter.printSummary() + } + + } + +} -- cgit v1.2.3