PageRenderTime 22ms CodeModel.GetById 26ms RepoModel.GetById 0ms app.codeStats 0ms

/src/compiler/scala/tools/ant/ScalaTool.scala

https://gitlab.com/mdimjasevic/scala-2.10-debian-package-take-2
Scala | 276 lines | 148 code | 39 blank | 89 comment | 42 complexity | 0443585d4e39bd7f13927f4277bb8d2f MD5 | raw file
  1. /* __ *\
  2. ** ________ ___ / / ___ Scala Ant Tasks **
  3. ** / __/ __// _ | / / / _ | (c) 2005-2013, LAMP/EPFL **
  4. ** __\ \/ /__/ __ |/ /__/ __ | http://scala-lang.org/ **
  5. ** /____/\___/_/ |_/____/_/ | | **
  6. ** |/ **
  7. \* */
  8. package scala.tools.ant
  9. import java.io.{File, InputStream, FileWriter}
  10. import org.apache.tools.ant.BuildException
  11. import org.apache.tools.ant.types.{Path, Reference}
  12. /** An Ant task that generates a shell or batch script to execute a
  13. * Scala program.
  14. *
  15. * This task can take the following parameters as attributes:
  16. * - `file` (mandatory),
  17. * - `class` (mandatory),
  18. * - `platforms`,
  19. * - `classpath`,
  20. * - `properties`,
  21. * - `javaflags`,
  22. * - `toolflags`.
  23. *
  24. * @author Gilles Dubochet
  25. * @version 1.1
  26. */
  27. class ScalaTool extends ScalaMatchingTask {
  28. private def emptyPath = new Path(getProject)
  29. /*============================================================================*\
  30. ** Ant user-properties **
  31. \*============================================================================*/
  32. abstract class PermissibleValue {
  33. val values: List[String]
  34. def isPermissible(value: String): Boolean =
  35. (value == "") || values.exists(_.startsWith(value))
  36. }
  37. /** Defines valid values for the platforms property. */
  38. object Platforms extends PermissibleValue {
  39. val values = List("unix", "windows")
  40. }
  41. /** The path to the exec script file. `".bat"` will be appended for the
  42. * Windows BAT file, if generated. */
  43. private var file: Option[File] = None
  44. /** The main class to run. */
  45. private var mainClass: Option[String] = None
  46. /** Supported platforms for the script. Either `"unix"` or `"windows"`.
  47. * Defaults to both. */
  48. private var platforms: List[String] = List("unix", "windows")
  49. /** An (optional) path to all JARs that this script depend on. Paths must be
  50. * relative to the scala home directory. If not set, all JAR archives and
  51. * folders in `"lib/"` are automatically added. */
  52. private var classpath: List[String] = Nil
  53. /** An (optional) path to JARs that this script depends on relative to the
  54. * ant project's `basedir`. */
  55. private var classpathPath: Path = emptyPath
  56. /** Comma-separated Java system properties to pass to the JRE. Properties
  57. * are formatted as `name=value`. Properties `scala.home`, `scala.tool.name`
  58. * and `scala.tool.version` are always set. */
  59. private var properties: List[(String, String)] = Nil
  60. /** Additional flags passed to the JRE (`"java [javaFlags] class"`). */
  61. private var javaFlags: String = ""
  62. /** Additional flags passed to the tool (`"java class [toolFlags]"`).
  63. * Can only be set when a main class is defined. */
  64. private var toolFlags: String = ""
  65. /*============================================================================*\
  66. ** Properties setters **
  67. \*============================================================================*/
  68. /** Sets the file attribute. */
  69. def setFile(input: File) =
  70. file = Some(input)
  71. /** Sets the main class attribute. */
  72. def setClass(input: String) =
  73. mainClass = Some(input)
  74. /** Sets the platforms attribute. */
  75. def setPlatforms(input: String) = {
  76. platforms = input.split(",").toList.flatMap { s: String =>
  77. val st = s.trim
  78. if (Platforms.isPermissible(st))
  79. (if (input != "") List(st) else Nil)
  80. else {
  81. buildError("Platform " + st + " does not exist.")
  82. }
  83. }
  84. }
  85. /** Sets the classpath with which to run the tool.
  86. *
  87. * Note that this mechanism of setting the classpath is generally preferred
  88. * for general purpose scripts, as this does not assume all elements are
  89. * relative to the Ant `basedir`. Additionally, the platform specific
  90. * demarcation of any script variables (e.g. `${SCALA_HOME}` or
  91. * `%SCALA_HOME%`) can be specified in a platform independant way (e.g.
  92. * `@SCALA_HOME@`) and automatically translated for you.
  93. */
  94. def setClassPath(input: String) {
  95. classpath = classpath ::: input.split(",").toList
  96. }
  97. /**
  98. * A special method that allows ant classpath path definitions to be nested
  99. * within this ant task.
  100. */
  101. def createClassPath: Path = classpathPath.createPath()
  102. /**
  103. * Adds an Ant Path reference to the tool's classpath.
  104. * Note that all entries in the path must exist either relative to the project
  105. * basedir or with an absolute path to a file in the filesystem. As a result,
  106. * this is not a mechanism for setting the classpath for more general use scripts.
  107. */
  108. def setClassPathRef(input: Reference) {
  109. val tmpPath = emptyPath
  110. tmpPath.setRefid(input)
  111. classpath = classpath ::: tmpPath.list.toList
  112. }
  113. /** Sets JVM properties that will be set whilst running the tool. */
  114. def setProperties(input: String) = {
  115. properties = input.split(",").toList.flatMap { s: String =>
  116. val st = s.trim
  117. val stArray = st.split("=", 2)
  118. if (stArray.length == 2) {
  119. if (input != "") List(Pair(stArray(0), stArray(1))) else Nil
  120. }
  121. else
  122. buildError("Property " + st + " is not formatted properly.")
  123. }
  124. }
  125. /** Sets flags to be passed to the Java interpreter. */
  126. def setJavaflags(input: String) =
  127. javaFlags = input.trim
  128. /** Sets flags to be passed to the tool. */
  129. def setToolflags(input: String) =
  130. toolFlags = input.trim
  131. /*============================================================================*\
  132. ** Properties getters **
  133. \*============================================================================*/
  134. /** Gets the value of the classpath attribute in a Scala-friendly form.
  135. * @return The class path as a list of files. */
  136. private def getUnixclasspath: String =
  137. transposeVariableMarkup(classpath.mkString("", ":", "").replace('\\', '/'), "${", "}")
  138. /** Gets the value of the classpath attribute in a Scala-friendly form.
  139. * @return The class path as a list of files. */
  140. private def getWinclasspath: String =
  141. transposeVariableMarkup(classpath.mkString("", ";", "").replace('/', '\\'), "%", "%")
  142. private def getProperties: String =
  143. properties.map({
  144. case Pair(name,value) => "-D" + name + "=\"" + value + "\""
  145. }).mkString("", " ", "")
  146. /*============================================================================*\
  147. ** Compilation and support methods **
  148. \*============================================================================*/
  149. // XXX encoding and generalize
  150. private def getResourceAsCharStream(clazz: Class[_], resource: String): Stream[Char] = {
  151. val stream = clazz.getClassLoader() getResourceAsStream resource
  152. if (stream == null) Stream.empty
  153. else Stream continually stream.read() takeWhile (_ != -1) map (_.asInstanceOf[Char])
  154. }
  155. // Converts a variable like @SCALA_HOME@ to ${SCALA_HOME} when pre = "${" and post = "}"
  156. private def transposeVariableMarkup(text: String, pre: String, post: String) : String = {
  157. val chars = scala.io.Source.fromString(text)
  158. val builder = new StringBuilder()
  159. while (chars.hasNext) {
  160. val char = chars.next
  161. if (char == '@') {
  162. var char = chars.next
  163. val token = new StringBuilder()
  164. while (chars.hasNext && char != '@') {
  165. token.append(char)
  166. char = chars.next
  167. }
  168. if (token.toString == "")
  169. builder.append('@')
  170. else
  171. builder.append(pre + token.toString + post)
  172. } else builder.append(char)
  173. }
  174. builder.toString
  175. }
  176. private def readAndPatchResource(resource: String, tokens: Map[String, String]): String = {
  177. val chars = getResourceAsCharStream(this.getClass, resource).iterator
  178. val builder = new StringBuilder()
  179. while (chars.hasNext) {
  180. val char = chars.next
  181. if (char == '@') {
  182. var char = chars.next
  183. val token = new StringBuilder()
  184. while (chars.hasNext && char != '@') {
  185. token.append(char)
  186. char = chars.next
  187. }
  188. if (tokens.contains(token.toString))
  189. builder.append(tokens(token.toString))
  190. else if (token.toString == "")
  191. builder.append('@')
  192. else
  193. builder.append("@" + token.toString + "@")
  194. } else builder.append(char)
  195. }
  196. builder.toString
  197. }
  198. private def writeFile(file: File, content: String) =
  199. if (file.exists() && !file.canWrite())
  200. buildError("File " + file + " is not writable")
  201. else {
  202. val writer = new FileWriter(file, false)
  203. writer write content
  204. writer.close()
  205. }
  206. /*============================================================================*\
  207. ** The big execute method **
  208. \*============================================================================*/
  209. /** Performs the tool creation. */
  210. override def execute() = {
  211. // Tests if all mandatory attributes are set and valid.
  212. if (file.isEmpty) buildError("Attribute 'file' is not set.")
  213. if (mainClass.isEmpty) buildError("Main class must be set.")
  214. val resourceRoot = "scala/tools/ant/templates/"
  215. val patches = Map (
  216. ("class", mainClass.get),
  217. ("properties", getProperties),
  218. ("javaflags", javaFlags),
  219. ("toolflags", toolFlags)
  220. )
  221. // Consolidate Paths into classpath
  222. classpath = classpath ::: classpathPath.list.toList
  223. // Generate the scripts
  224. if (platforms contains "unix") {
  225. val unixPatches = patches + (("classpath", getUnixclasspath))
  226. val unixTemplateResource = resourceRoot + "tool-unix.tmpl"
  227. val unixTemplate = readAndPatchResource(unixTemplateResource, unixPatches)
  228. writeFile(file.get, unixTemplate)
  229. }
  230. if (platforms contains "windows") {
  231. val winPatches = patches + (("classpath", getWinclasspath))
  232. val winTemplateResource = resourceRoot + "tool-windows.tmpl"
  233. val winTemplate = readAndPatchResource(winTemplateResource, winPatches)
  234. writeFile(new File(file.get.getAbsolutePath() + ".bat"), winTemplate)
  235. }
  236. }
  237. }