1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 23 24 25 26 27 28 29 30 31 32 33 34 35 36 37 38 39 40 41 42 43 44 45 46 47 48 49 50 51 52 53 54 55 56 57 58 59 60 61 62 63 64 65 66 67 68 69 70 71 72 73 74 75 76 77 78 79 80 81 82 83 84 85 86 87 88 89 90 91 92 93 94 95 96 97 98 99 100 101 102 103 104 105 106 107 108 109 110 111 112 113 114 115 116 117 118 119 120 121 122 123 124 125 126 127 128 129 130 131 132 133 134 135 136 137 138 139 140 141 142 143 144 145 146 147 148 149 150 151 152 153 154 155 156 157 158 159 160 161 162 163 164 165 166 167 168 169 170 171 172 173 174 175 176 177 178 179
|
import java.io.{FileInputStream, File, InputStream, FileWriter}
import sbt._
import scala.io._
/**
* Create the scala binaries
* Based on scala.tools.ant.ScalaTool
* @author Grégory Moix (for the sbt adaptation)
*/
trait ScalaTools {
self: BasicLayer =>
lazy val templatesLocation = compilerConfig.srcDir/ "scala" / "tools" / "ant" / "templates"
lazy val unixTemplate = templatesLocation / "tool-unix.tmpl"
lazy val winTemplate = templatesLocation / "tool-windows.tmpl"
// XXX encoding and generalize
private def getResourceAsCharStream(resource: Path): Stream[Char] = {
val stream = new FileInputStream(resource.asFile)
def streamReader(): Stream[Char] = stream.read match {
case -1 => Stream.empty
case value => Stream.cons(value.asInstanceOf[Char], streamReader())
}
if (stream == null) {
log.debug("Stream was null")
Stream.empty
}
//else Stream continually stream.read() takeWhile (_ != -1) map (_.asInstanceOf[Char]) // Does not work in scala 2.7.7
else streamReader
}
// Converts a variable like @SCALA_HOME@ to ${SCALA_HOME} when pre = "${" and post = "}"
private def transposeVariableMarkup(text: String, pre: String, post: String) : String = {
val chars = Source.fromString(text)
val builder = new StringBuilder()
while (chars.hasNext) {
val char = chars.next
if (char == '@') {
var char = chars.next
val token = new StringBuilder()
while (chars.hasNext && char != '@') {
token.append(char)
char = chars.next
}
if (token.toString == "")
builder.append('@')
else
builder.append(pre + token.toString + post)
} else builder.append(char)
}
builder.toString
}
private def readAndPatchResource(resource: Path, tokens: Map[String, String]): String = {
val chars = getResourceAsCharStream(resource).elements
val builder = new StringBuilder()
while (chars.hasNext) {
val char = chars.next
if (char == '@') {
var char = chars.next
val token = new StringBuilder()
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, makeExecutable: Boolean): Option[String] =
if (file.exists() && !file.canWrite())
Some("File " + file + " is not writable")
else {
val writer = new FileWriter(file, false)
writer.write(content)
writer.close()
file.setExecutable(makeExecutable)
None
}
/** Gets the value of the classpath attribute in a Scala-friendly form.
* @return The class path as a list of files. */
private def getUnixclasspath(classpath: List[String]): String =
transposeVariableMarkup(classpath.mkString("", ":", "").replace('\\', '/'), "${", "}")
/** Gets the value of the classpath attribute in a Scala-friendly form.
* @return The class path as a list of files. */
private def getWinclasspath(classpath: List[String]): String =
transposeVariableMarkup(classpath.mkString("", ";", "").replace('/', '\\'), "%", "%")
/** Performs the tool creation of a tool with for a given os
* @param file
* @param mainClas
* @param properties
* @param javaFlags
* @param toolFlags
* @param classPath
* @param template
* @param classpathFormater
*/
private def tool(template: Path, classpathFormater: List[String] => String, file: Path, mainClass: String,
properties: String, javaFlags: String, toolFlags: String, classPath: List[Path], makeExecutable: Boolean): Option[String] = {
val patches = Map (
("class", mainClass),
("properties", properties),
("javaflags", javaFlags),
("toolflags", toolFlags),
("classpath", classpathFormater(classPath.map(_.absolutePath)))
)
val result = readAndPatchResource(template, patches)
writeFile(file.asFile, result, makeExecutable)
}
private def generateTool(config: ToolConfiguration): Option[String] =
generateTool(config.toolName, config.destination, config.mainClass, config.properties, config.javaFlags, config.toolFlags, config.classPath)
private def generateTool(toolName: String, destination: Path, mainClass: String, properties: String, javaFlags: String, toolFlags: String, classPath: List[Path]): Option[String] ={
val unixFile = destination / toolName
val winFile = destination /(toolName + ".bat")
tool(unixTemplate, getUnixclasspath, unixFile, mainClass, properties, javaFlags, toolFlags, classPath, true) orElse
tool(winTemplate, getWinclasspath, winFile, mainClass, properties, javaFlags, toolFlags, classPath, false)
}
/*============================================================================*\
** Definition of the different tools **
\*============================================================================*/
private val defaultJavaFlags = "-Xmx256M -Xms32M"
/**
* A class that holds the different parameters of a tool
*/
class ToolConfiguration(val toolName: String, val destination: Path, val mainClass: String, val properties: String, val javaFlags: String, val toolFlags: String, val classPath: List[Path])
/**
* Generate all tools
* @param destination Root folder where all the binaries will be written
* @param classpath Should be specified when you want to use a specific classpath, could be Nil if you want
* to make the bin use what is in the lib folder of the distribution.
*/
def tools(destination: Path, classpath: List[Path]) = task {
val scala = new ToolConfiguration("scala", destination, "scala.tools.nsc.MainGenericRunner", "",defaultJavaFlags, "", classpath)
val scalac = new ToolConfiguration("scalac", destination, "scala.tools.nsc.Main", "",defaultJavaFlags, "", classpath)
val scaladoc = new ToolConfiguration("scaladoc",destination,"scala.tools.nsc.ScalaDoc", "",defaultJavaFlags,"", classpath)
val fsc = new ToolConfiguration("fsc", destination,"scala.tools.nsc.CompileClient", "",defaultJavaFlags, "", classpath)
val scalap = new ToolConfiguration("scalap",destination, "scala.tools.scalap.Main", "",defaultJavaFlags, "", classpath)
val toolList = scala :: scalac :: scaladoc :: fsc :: scalap :: Nil
def process(list: List[ToolConfiguration]): Option[String] = list match {
case x :: xs => {
log.debug("Generating "+x.toolName+" bin")
generateTool(x) orElse process(xs)
}
case Nil => None
}
FileUtilities.createDirectory(destination, log)
process(toolList)
}
}
|