diff options
author | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2006-01-09 13:07:02 +0000 |
---|---|---|
committer | Gilles Dubochet <gilles.dubochet@epfl.ch> | 2006-01-09 13:07:02 +0000 |
commit | ee5a600ff41d39cf96fc224accd82b245394d814 (patch) | |
tree | c65732611822d944138cb451ae467c129283193a /src/compiler/scala/tools/ant/ScalaTool.scala | |
parent | 5d4d8b21cec21fe23490e66182a9b4890e15a9d1 (diff) | |
download | scala-ee5a600ff41d39cf96fc224accd82b245394d814.tar.gz scala-ee5a600ff41d39cf96fc224accd82b245394d814.tar.bz2 scala-ee5a600ff41d39cf96fc224accd82b245394d814.zip |
Updated the Ant build system:
1. Minor aesthetic changes to Scalac.
2. Added Ant task ScalaTool that automatically constructs wrappers for a tool. Wrapper templates are in ant/templates.
3. Modified build.xml to use ScalaTool.
4. Removed nsc/ant/NSC which is now obsolete (replaced by ant/Scalac).
5. Added a antlib.xml file that allows to load all ant tasks in one operation in an ant script.
Diffstat (limited to 'src/compiler/scala/tools/ant/ScalaTool.scala')
-rw-r--r-- | src/compiler/scala/tools/ant/ScalaTool.scala | 304 |
1 files changed, 304 insertions, 0 deletions
diff --git a/src/compiler/scala/tools/ant/ScalaTool.scala b/src/compiler/scala/tools/ant/ScalaTool.scala new file mode 100644 index 0000000000..b079fde680 --- /dev/null +++ b/src/compiler/scala/tools/ant/ScalaTool.scala @@ -0,0 +1,304 @@ +/* __ ______________ *\ +** / |/ / ____/ ____/ ** +** / | | /___ / /___ ** +** /_/|__/_____/_____/ Copyright 2005-2006 LAMP/EPFL ** +** +** $Id$ +\* */ + +package scala.tools.ant { + + import scala.collection.immutable.{Map, ListMap} + + import java.io.{File, InputStream, FileWriter} + 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} + + /** An Ant task that generates a SH or BAT script to execute a Scala program. + * This task can take the following parameters as attributes:<ul> + * <li>file (mandatory),</li> + * <li>name,</li> + * <li>class (mandatory),</li> + * <li>platforms,</li> + * <li>version,</li> + * <li>copyright,</li> + * <li>classpath,</li> + * <li>properties,</li> + * <li>javaFlags,</li> + * <li>toolFlags,</li> + * <li>genericFile.</li></ul> + * + * @author Gilles Dubochet */ + class ScalaTool extends MatchingTask { + + /** 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 platforms property. */ + object Platforms extends PermissibleValue { + val values = List("unix", "windows") + } + + /** The path to the exec script file. ".bat" will be appended for the + * Windows BAT file, if generated. */ + private var file: Option[File] = None + /** The name of this tool. By default this is equal to the file name. */ + private var name: Option[String] = None + /** The main class to run. */ + private var mainClass: Option[String] = None + /** Supported platforms for the script. Either "unix" or "windows". Defaults + * to both. */ + private var platforms: List[String] = Nil + /** The optional version number. If set, when "-version" is passed to the + * script, this value will be printed. */ + private var version: String = "" + /** The optional copyright notice, that will be printed in the script. */ + private var copyright: String = "This file is copyrighted by its owner" + /** An (optional) path to all JARs that this script depend on. Paths must be + * relative to the scala home directory. If not set, all JAR archives in + * "lib/" are automatically added. */ + private var classpath: List[String] = Nil + /** Comma-separated Java system properties to pass to the JRE. Properties + * are formated as name=value. Properties scala.home and scala.tool.name + * are always set. */ + private var properties: List[Pair[String,String]] = Nil + /** Additional flags passed to the JRE ("java [javaFlags] class"). */ + private var javaFlags: String = "" + /** Additional flags passed to the tool ("java class [toolFlags]"). */ + private var toolFlags: String = "" + /** The path to the generic runtime script file. ".bat" will be appended for + * the Windows BAT file, if generated. If not set, no generic runtime + * script will be generated. */ + private var genericFile: Option[File] = None + +/******************************************************************************\ +** Properties setters ** +\******************************************************************************/ + + /** Sets the file attribute. Used by Ant. + * @param input The value of <code>file</code>. */ + def setFile(input: File) = + file = Some(input) + + /** Sets the file attribute. Used by Ant. + * @param input The value of <code>file</code>. */ + def setName(input: String) = + name = Some(input) + + /** Sets the main class attribute. Used by Ant. + * @param input The value of <code>mainClass</code>. */ + def setClass(input: String) = + mainClass = Some(input) + + /** Sets the platforms attribute. Used by Ant. + * @param input The value for <code>platforms</code>. */ + def setPlatforms(input: String) = { + platforms = List.fromArray(input.split(",")).flatMap(s: String => { + val st = s.trim() + if (Platforms.isPermissible(st)) + (if (input != "") List(st) else Nil) + else { + error("Platform " + st + " does not exist.") + Nil + } + }) + } + + /** Sets the version attribute. Used by Ant. + * @param input The value of <code>version</code>. */ + def setVersion(input: String) = + version = input + + /** Sets the copyright attribute. Used by Ant. + * @param input The value of <code>copyright</code>. */ + def setCopyright(input: String) = + copyright = input + + /** Sets the classpath attribute. Used by Ant. + * @param input The value of <code>classpath</code>. */ + def setClasspath(input: String) = + classpath = classpath ::: List.fromArray(input.split(":")) + + /** Sets the properties attribute. Used by Ant. + * @param input The value for <code>properties</code>. */ + def setProperties(input: String) = { + properties = List.fromArray(input.split(",")).flatMap(s: String => { + val st = s.trim(); val stArray = st.split("=", 2) + if (stArray.length == 2) { + if (input != "") List(Pair(stArray(0), stArray(1))) else Nil + } else error("Property " + st + " does not conform to specification.") + }) + } + + /** Sets the version attribute. Used by Ant. + * @param input The value of <code>version</code>. */ + def setJavaFlags(input: String) = + javaFlags = input + + /** Sets the version attribute. Used by Ant. + * @param input The value of <code>version</code>. */ + def setToolFlags(input: String) = + toolFlags = input + + /** Sets the version attribute. Used by Ant. + * @param input The value of <code>version</code>. */ + def setGenericFile(input: File) = + genericFile = Some(input) + +/******************************************************************************\ +** Properties getters ** +\******************************************************************************/ + + /** Gets the value of the file attribute in a Scala-friendly form. + * @returns The file as a file. */ + private def getFile: File = + if (file.isEmpty) error("Member 'file' is empty.") + else getProject().resolveFile(file.get.toString()) + + /** Gets the value of the classpath attribute in a Scala-friendly form. + * @returns The class path as a list of files. */ + private def getUnixClasspath: String = + classpath.mkString("", ":", "") + + /** Gets the value of the classpath attribute in a Scala-friendly form. + * @returns The class path as a list of files. */ + private def getWinClasspath: String = + classpath.mkString("", ";", "") + + /** Gets the value of the classpath attribute in a Scala-friendly form. + * @returns The class path as a list of files. */ + private def getProperties: String = + properties.map({ + case Pair(name,value) => "-D" + name + "=\"" + value + "\"" + }).mkString("", " ", "") + + /** Gets the value of the file attribute in a Scala-friendly form. + * @returns The file as a file. */ + private def getGenericFile: File = + if (genericFile.isEmpty) error("Member 'file' is empty.") + else getProject().resolveFile(genericFile.get.toString()) + +/******************************************************************************\ +** Compilation and support methods ** +\******************************************************************************/ + + /** 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()) + + private def readResource(resource: String, + tokens: Map[String, String] + ): String = { + val chars = new Iterator[Char] { + private val stream = + this.getClass().getClassLoader().getResourceAsStream(resource) + private def readStream(): Char = stream.read().asInstanceOf[Char] + private var buf: Char = readStream() + def hasNext: Boolean = (buf != (-1.).asInstanceOf[Char]) + def next: Char = { + val bufbuf = buf + buf = readStream() + bufbuf + } + } + val builder = new StringBuffer() + while (chars.hasNext) { + val char = chars.next + if (char == '@') { + var char = chars.next + val token = new StringBuffer() + while (chars.hasNext && char != '@') { + token.append(char) + char = chars.next + } + if (tokens.contains(token.toString())) + builder.append(tokens(token.toString())) + else if (token.toString() == "") + builder.append('@') + else + builder.append("@" + token.toString() + "@") + } else builder.append(char) + } + builder.toString() + } + + private def writeFile(file: File, content: String) = + if (file.exists() && !file.canWrite()) + error("File " + file + " is not writable") + else { + val writer = new FileWriter(file, false) + writer.write(content) + writer.close() + } + + +/******************************************************************************\ +** The big execute method ** +\******************************************************************************/ + + /** Performs the compilation. */ + override def execute() = { + // Tests if all mandatory attributes are set and valid. + if (file.isEmpty) error("Attribute 'file' is not set.") + if (name.isEmpty) name = Some(file.get.getName()) + if (mainClass.isEmpty) error("Attribute 'mainclass' is not set.") + if (platforms.isEmpty) platforms = Platforms.values + // Gets the input streams for the script templates. + val resourceRoot = "scala/tools/ant/templates/" + val patches = ListMap.Empty. + update("name", name.get). + update("class", mainClass.get). + update("version", version). + update("copyright", copyright). + update("properties", getProperties). + update("javaflags", javaFlags). + update("toolflags", toolFlags) + if (platforms.contains("unix")) { + val unixPatches = patches.update("classpath", getUnixClasspath) + val unixTemplateResource = resourceRoot + "tool-unix.tmpl" + val unixTemplate = readResource(unixTemplateResource, unixPatches) + writeFile(getFile, unixTemplate) + if (!genericFile.isEmpty) { + val unixTemplateResource = resourceRoot + "generic-unix.tmpl" + val unixTemplate = readResource(unixTemplateResource, unixPatches) + writeFile(getGenericFile, unixTemplate) + } + } + if (platforms.contains("windows")) { + val winPatches = patches.update("classpath", getWinClasspath) + val winTemplateResource = resourceRoot + "tool-windows.tmpl" + val winTemplate = readResource(winTemplateResource, winPatches) + writeFile(new File(getFile.getAbsolutePath() + ".bat"), winTemplate) + if (!genericFile.isEmpty) { + val winTemplateResource = resourceRoot + "generic-windows.tmpl" + val winTemplate = readResource(winTemplateResource, winPatches) + writeFile(new File(getGenericFile.getAbsolutePath() + ".bat"), + winTemplate) + } + } + } + + } + +} |