mirror of https://github.com/sbt/sbt.git
Cut size of final jar by 300k.
This commit is contained in:
parent
f045123989
commit
33434bc82b
|
|
@ -12,7 +12,7 @@ object Boot
|
||||||
{
|
{
|
||||||
System.clearProperty("scala.home") // avoid errors from mixing Scala versions in the same JVM
|
System.clearProperty("scala.home") // avoid errors from mixing Scala versions in the same JVM
|
||||||
CheckProxy()
|
CheckProxy()
|
||||||
try { Launch(args) }
|
try { Launch(args.toList) }
|
||||||
catch
|
catch
|
||||||
{
|
{
|
||||||
case b: BootException => errorAndExit(b)
|
case b: BootException => errorAndExit(b)
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,19 @@
|
||||||
|
package xsbt.boot
|
||||||
|
|
||||||
|
import java.util.HashMap
|
||||||
|
|
||||||
|
final class Cache[K,V <: AnyRef](create: K => V) extends NotNull
|
||||||
|
{
|
||||||
|
private[this] val delegate = new HashMap[K,V]
|
||||||
|
def apply(k: K): V =
|
||||||
|
{
|
||||||
|
val existing = delegate.get(k)
|
||||||
|
if(existing eq null) newEntry(k) else existing
|
||||||
|
}
|
||||||
|
private[this] def newEntry(k: K): V =
|
||||||
|
{
|
||||||
|
val v = create(k)
|
||||||
|
delegate.put(k, v)
|
||||||
|
v
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -3,6 +3,7 @@
|
||||||
*/
|
*/
|
||||||
package xsbt.boot
|
package xsbt.boot
|
||||||
|
|
||||||
|
import Pre._
|
||||||
import java.net.{MalformedURLException, URL}
|
import java.net.{MalformedURLException, URL}
|
||||||
|
|
||||||
object CheckProxy
|
object CheckProxy
|
||||||
|
|
@ -33,5 +34,5 @@ object CheckProxy
|
||||||
private def copyEnv(envKey: String, sysKey: String) { setProperty(sysKey, System.getenv(envKey)) }
|
private def copyEnv(envKey: String, sysKey: String) { setProperty(sysKey, System.getenv(envKey)) }
|
||||||
private def setProperty(key: String, value: String) { if(value != null) System.setProperty(key, value) }
|
private def setProperty(key: String, value: String) { if(value != null) System.setProperty(key, value) }
|
||||||
private def isPropertyDefined(k: String) = isDefined(System.getProperty(k))
|
private def isPropertyDefined(k: String) = isDefined(System.getProperty(k))
|
||||||
private def isDefined(s: String) = s != null && !s.isEmpty
|
private def isDefined(s: String) = s != null && isNonEmpty(s)
|
||||||
}
|
}
|
||||||
|
|
@ -1,15 +1,16 @@
|
||||||
package xsbt.boot
|
package xsbt.boot
|
||||||
|
|
||||||
|
import Pre._
|
||||||
import java.io.{File, FileInputStream, InputStreamReader}
|
import java.io.{File, FileInputStream, InputStreamReader}
|
||||||
import java.net.{URI, URL}
|
import java.net.{URI, URL}
|
||||||
|
|
||||||
object Configuration
|
object Configuration
|
||||||
{
|
{
|
||||||
def parse(file: URL, baseDirectory: File) = Using( new InputStreamReader(file.openStream, "utf8") )( (new ConfigurationParser).apply )
|
def parse(file: URL, baseDirectory: File) = Using( new InputStreamReader(file.openStream, "utf8") )( (new ConfigurationParser).apply )
|
||||||
def find(args: Seq[String], baseDirectory: File): (URL, Seq[String]) =
|
def find(args: List[String], baseDirectory: File): (URL, List[String]) =
|
||||||
args match
|
args match
|
||||||
{
|
{
|
||||||
case Seq(head, tail @ _*) if head.startsWith("@")=> (configurationFromFile(head.substring(1), baseDirectory), tail)
|
case head :: tail if head.startsWith("@")=> (configurationFromFile(head.substring(1), baseDirectory), tail)
|
||||||
case _ =>
|
case _ =>
|
||||||
val propertyConfigured = System.getProperty("sbt.boot.properties")
|
val propertyConfigured = System.getProperty("sbt.boot.properties")
|
||||||
val url = if(propertyConfigured == null) configurationOnClasspath else configurationFromFile(propertyConfigured, baseDirectory)
|
val url = if(propertyConfigured == null) configurationOnClasspath else configurationFromFile(propertyConfigured, baseDirectory)
|
||||||
|
|
@ -17,7 +18,7 @@ object Configuration
|
||||||
}
|
}
|
||||||
def configurationOnClasspath: URL =
|
def configurationOnClasspath: URL =
|
||||||
{
|
{
|
||||||
resourcePaths.toStream.map(getClass.getResource).find(_ ne null) getOrElse
|
resourcePaths.elements.map(getClass.getResource).find(_ ne null) getOrElse
|
||||||
( multiPartError("Could not finder sbt launch configuration. Searched classpath for:", resourcePaths))
|
( multiPartError("Could not finder sbt launch configuration. Searched classpath for:", resourcePaths))
|
||||||
}
|
}
|
||||||
def configurationFromFile(path: String, baseDirectory: File): URL =
|
def configurationFromFile(path: String, baseDirectory: File): URL =
|
||||||
|
|
@ -29,21 +30,23 @@ object Configuration
|
||||||
if(exists) Some(resolved.toURL) else None
|
if(exists) Some(resolved.toURL) else None
|
||||||
}
|
}
|
||||||
val against = resolveAgainst(baseDirectory)
|
val against = resolveAgainst(baseDirectory)
|
||||||
against.toStream.flatMap(resolve).firstOption.getOrElse(multiPartError("Could not find configuration file '" + path + "'. Searched:", against))
|
val resolving = against.elements.flatMap(e => resolve(e).toList.elements)
|
||||||
|
if(!resolving.hasNext) multiPartError("Could not find configuration file '" + path + "'. Searched:", against)
|
||||||
|
resolving.next()
|
||||||
}
|
}
|
||||||
def multiPartError[T](firstLine: String, lines: Seq[T]) = throw new BootException( (Seq(firstLine) ++ lines).mkString("\n\t") )
|
def multiPartError[T](firstLine: String, lines: List[T]) = error( (firstLine :: lines).mkString("\n\t") )
|
||||||
|
|
||||||
val ConfigurationName = "sbt.boot.properties"
|
val ConfigurationName = "sbt.boot.properties"
|
||||||
val JarBasePath = "/sbt/"
|
val JarBasePath = "/sbt/"
|
||||||
def userConfigurationPath = "/" + ConfigurationName
|
def userConfigurationPath = "/" + ConfigurationName
|
||||||
def defaultConfigurationPath = JarBasePath + ConfigurationName
|
def defaultConfigurationPath = JarBasePath + ConfigurationName
|
||||||
def resourcePaths = Seq(userConfigurationPath, defaultConfigurationPath)
|
def resourcePaths = List(userConfigurationPath, defaultConfigurationPath)
|
||||||
def resolveAgainst(baseDirectory: File) = Seq(baseDirectory toURI, new File(System.getProperty("user.home")) toURI, classLocation(getClass).toURI)
|
def resolveAgainst(baseDirectory: File) = List(baseDirectory toURI, new File(System.getProperty("user.home")) toURI, classLocation(getClass).toURI)
|
||||||
|
|
||||||
def classLocation(cl: Class[_]): URL =
|
def classLocation(cl: Class[_]): URL =
|
||||||
{
|
{
|
||||||
val codeSource = cl.getProtectionDomain.getCodeSource
|
val codeSource = cl.getProtectionDomain.getCodeSource
|
||||||
if(codeSource == null) throw new BootException("No class location for " + cl)
|
if(codeSource == null) error("No class location for " + cl)
|
||||||
else codeSource.getLocation
|
else codeSource.getLocation
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,28 +1,26 @@
|
||||||
package xsbt.boot
|
package xsbt.boot
|
||||||
|
|
||||||
import scala.util.parsing.combinator.Parsers
|
import Pre._
|
||||||
import scala.util.parsing.input.{Reader, StreamReader}
|
|
||||||
|
|
||||||
import java.lang.Character.isWhitespace
|
import java.lang.Character.isWhitespace
|
||||||
import java.io.File
|
import java.io.{BufferedReader, File, FileInputStream, InputStreamReader, Reader, StringReader}
|
||||||
import java.net.{MalformedURLException, URL}
|
import java.net.{MalformedURLException, URL}
|
||||||
import scala.collection.immutable.TreeMap
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
class ConfigurationParser extends Parsers with NotNull
|
class ConfigurationParser extends NotNull
|
||||||
{
|
{
|
||||||
import scala.util.parsing.input.CharSequenceReader
|
def apply(file: File): LaunchConfiguration = Using(new InputStreamReader(new FileInputStream(file), "UTF-8"))(apply)
|
||||||
def apply(s: String): LaunchConfiguration = processResult(configuration(new CharSequenceReader(s, 0)))
|
def apply(s: String): LaunchConfiguration = Using(new StringReader(s))(apply)
|
||||||
def apply(source: java.io.Reader): LaunchConfiguration = processResult(configuration(StreamReader(source)))
|
def apply(reader: Reader): LaunchConfiguration = Using(new BufferedReader(reader))(apply)
|
||||||
|
private def apply(in: BufferedReader): LaunchConfiguration =
|
||||||
def processResult(r: ParseResult[LaunchConfiguration]) =
|
{
|
||||||
r match
|
def readLine(accum: List[Line], index: Int): List[Line] =
|
||||||
{
|
{
|
||||||
case Success(r, _) => r
|
val line = in.readLine()
|
||||||
case _ => throw new BootException(r.toString)
|
if(line eq null) accum.reverse else readLine(ParseLine(line, index) ::: accum, index+1)
|
||||||
}
|
}
|
||||||
|
processSections(processLines(readLine(Nil, 0)))
|
||||||
|
}
|
||||||
// section -> configuration instance processing
|
// section -> configuration instance processing
|
||||||
lazy val configuration = phrase(sections ^^ processSections)
|
|
||||||
def processSections(sections: SectionMap): LaunchConfiguration =
|
def processSections(sections: SectionMap): LaunchConfiguration =
|
||||||
{
|
{
|
||||||
val (scalaVersion, m1) = processSection(sections, "scala", getScalaVersion)
|
val (scalaVersion, m1) = processSection(sections, "scala", getScalaVersion)
|
||||||
|
|
@ -39,32 +37,33 @@ class ConfigurationParser extends Parsers with NotNull
|
||||||
def processVersion(value: Option[String]): Version = value.map(version).getOrElse(Version.default)
|
def processVersion(value: Option[String]): Version = value.map(version).getOrElse(Version.default)
|
||||||
def version(value: String): Version =
|
def version(value: String): Version =
|
||||||
{
|
{
|
||||||
if(value.isEmpty) throw new BootException("Version cannot be empty (omit version declaration to use the default version)")
|
if(isEmpty(value)) error("Version cannot be empty (omit version declaration to use the default version)")
|
||||||
val tokens = trim(value.split(",", 2))
|
val tokens = trim(value.split(",", 2))
|
||||||
import Version.{Explicit, Implicit}
|
import Version.{Explicit, Implicit}
|
||||||
val defaultVersion = if(tokens.length == 2) Some(tokens(1)) else None
|
val defaultVersion = if(tokens.length == 2) Some(tokens(1)) else None
|
||||||
Implicit(tokens(0), defaultVersion).fold(err => Explicit(tokens(0)), identity[Implicit])
|
Implicit(tokens(0), defaultVersion)(err => new Explicit(tokens(0)))
|
||||||
}
|
}
|
||||||
def processSection[T](sections: Map[String, LabelMap], name: String, f: LabelMap => T) =
|
def processSection[T](sections: SectionMap, name: String, f: LabelMap => T) =
|
||||||
process[String,LabelMap,T](sections, name, m => f(m withDefaultValue(None)))
|
process[String,LabelMap,T](sections, name, m => f(m default(x => None)))
|
||||||
def process[K,V,T](sections: Map[K,V], name: K, f: V => T): (T, Map[K,V]) = ( f(sections(name)), sections - name)
|
def process[K,V,T](sections: ListMap[K,V], name: K, f: V => T): (T, ListMap[K,V]) = ( f(sections(name)), sections - name)
|
||||||
def check(map: Map[String, _], label: String): Unit = if(map.isEmpty) () else { throw new BootException(map.keys.mkString("Invalid " + label + "(s): ", ",","")) }
|
def check(map: ListMap[String, _], label: String): Unit = if(map.isEmpty) () else error(map.keys.mkString("Invalid " + label + "(s): ", ",",""))
|
||||||
def check[T](label: String, pair: (T, Map[String, _])): T = { check(pair._2, label); pair._1 }
|
def check[T](label: String, pair: (T, ListMap[String, _])): T = { check(pair._2, label); pair._1 }
|
||||||
def id(map: LabelMap, name: String, default: String): (String, LabelMap) =
|
def id(map: LabelMap, name: String, default: String): (String, LabelMap) =
|
||||||
(map.getOrElse(name, None).getOrElse(default), map - name)
|
(getOrNone(map, name).getOrElse(default), map - name)
|
||||||
def ids(map: LabelMap, name: String, default: Seq[String]) =
|
def getOrNone[K,V](map: ListMap[K,Option[V]], k: K) = map.get(k).getOrElse(None)
|
||||||
|
def ids(map: LabelMap, name: String, default: List[String]) =
|
||||||
{
|
{
|
||||||
val result = map(name).map(value => trim(value.split(",")).filter(!_.isEmpty)).getOrElse(default)
|
val result = map(name).map(value => trim(value.split(",")).filter(isNonEmpty)).getOrElse(default)
|
||||||
(result, map - name)
|
(result, map - name)
|
||||||
}
|
}
|
||||||
def bool(map: LabelMap, name: String, default: Boolean): (Boolean, LabelMap) =
|
def bool(map: LabelMap, name: String, default: Boolean): (Boolean, LabelMap) =
|
||||||
{
|
{
|
||||||
val (b, m) = id(map, name, default.toString)
|
val (b, m) = id(map, name, default.toString)
|
||||||
(b.toBoolean, m)
|
(toBoolean(b), m)
|
||||||
}
|
}
|
||||||
def toFile(path: String): File = new File(path)// if the path is relative, it will be resolve by Launch later
|
def toFile(path: String): File = new File(path)// if the path is relative, it will be resolve by Launch later
|
||||||
def file(map: LabelMap, name: String, default: File): (File, LabelMap) =
|
def file(map: LabelMap, name: String, default: File): (File, LabelMap) =
|
||||||
(map.getOrElse(name, None).map(toFile).getOrElse(default), map - name)
|
(getOrNone(map, name).map(toFile).getOrElse(default), map - name)
|
||||||
|
|
||||||
def getBoot(m: LabelMap): BootSetup =
|
def getBoot(m: LabelMap): BootSetup =
|
||||||
{
|
{
|
||||||
|
|
@ -78,13 +77,13 @@ class ConfigurationParser extends Parsers with NotNull
|
||||||
BootSetup(dir, props, search, promptCreate, enableQuick, promptFill)
|
BootSetup(dir, props, search, promptCreate, enableQuick, promptFill)
|
||||||
}
|
}
|
||||||
def getLogging(m: LabelMap): Logging = check("label", process(m, "level", getLevel))
|
def getLogging(m: LabelMap): Logging = check("label", process(m, "level", getLevel))
|
||||||
def getLevel(m: Option[String]) = m.map(LogLevel.apply).getOrElse(Logging(LogLevel.Info))
|
def getLevel(m: Option[String]) = m.map(LogLevel.apply).getOrElse(new Logging(LogLevel.Info))
|
||||||
def getSearch(m: LabelMap, defaultPath: File): (Search, LabelMap) =
|
def getSearch(m: LabelMap, defaultPath: File): (Search, LabelMap) =
|
||||||
ids(m, "search", Nil) match
|
ids(m, "search", Nil) match
|
||||||
{
|
{
|
||||||
case (Nil, newM) => (Search.none, newM)
|
case (Nil, newM) => (Search.none, newM)
|
||||||
case (Seq(tpe), newM) => (Search(tpe, Seq(defaultPath)), newM)
|
case (tpe :: Nil, newM) => (Search(tpe, List(defaultPath)), newM)
|
||||||
case (Seq(tpe, paths @ _ *), newM) => (Search(tpe, paths.map(toFile)), newM)
|
case (tpe :: paths, newM) => (Search(tpe, paths.map(toFile)), newM)
|
||||||
}
|
}
|
||||||
|
|
||||||
def getApplication(m: LabelMap): Application =
|
def getApplication(m: LabelMap): Application =
|
||||||
|
|
@ -93,125 +92,118 @@ class ConfigurationParser extends Parsers with NotNull
|
||||||
val (name, m2) = id(m1, "name", "sbt")
|
val (name, m2) = id(m1, "name", "sbt")
|
||||||
val (rev, m3) = getVersion(m2)
|
val (rev, m3) = getVersion(m2)
|
||||||
val (main, m4) = id(m3, "class", "xsbt.Main")
|
val (main, m4) = id(m3, "class", "xsbt.Main")
|
||||||
val (components, m5) = ids(m4, "components", Seq("default"))
|
val (components, m5) = ids(m4, "components", List("default"))
|
||||||
val (crossVersioned, m6) = id(m5, "cross-versioned", "true")
|
val (crossVersioned, m6) = id(m5, "cross-versioned", "true")
|
||||||
check(m6, "label")
|
check(m6, "label")
|
||||||
new Application(org, name, rev, main, components, crossVersioned.toBoolean)
|
new Application(org, name, rev, main, components, toBoolean(crossVersioned))
|
||||||
}
|
}
|
||||||
def getRepositories(m: LabelMap): Seq[Repository] =
|
def getRepositories(m: LabelMap): List[Repository] =
|
||||||
{
|
{
|
||||||
import Repository.{Ivy, Maven, Predefined}
|
import Repository.{Ivy, Maven, Predefined}
|
||||||
m.toSeq.map {
|
m.toList.map {
|
||||||
case (key, None) => Predefined(key)
|
case (key, None) => Predefined(key)
|
||||||
case (key, Some(value)) =>
|
case (key, Some(value)) =>
|
||||||
val r = trim(value.split(",",2))
|
val r = trim(value.split(",",2))
|
||||||
val url = try { new URL(r(0)) } catch { case e: MalformedURLException => throw new BootException("Invalid URL specified for '" + key + "': " + e.getMessage) }
|
val url = try { new URL(r(0)) } catch { case e: MalformedURLException => error("Invalid URL specified for '" + key + "': " + e.getMessage) }
|
||||||
if(r.length == 2) Ivy(key, url, r(1)) else Maven(key, url)
|
if(r.length == 2) Ivy(key, url, r(1)) else Maven(key, url)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def getAppProperties(m: LabelMap): Seq[AppProperty] =
|
def getAppProperties(m: LabelMap): List[AppProperty] =
|
||||||
for((name, Some(value)) <- m.toSeq) yield
|
for((name, Some(value)) <- m.toList) yield
|
||||||
{
|
{
|
||||||
val map = Map() ++ trim(value.split(",")).map(parsePropertyDefinition(name))
|
val map = ListMap( trim(value.split(",")).map(parsePropertyDefinition(name)) : _*)
|
||||||
AppProperty(name)(map.get("quick"), map.get("new"), map.get("fill"))
|
AppProperty(name)(map.get("quick"), map.get("new"), map.get("fill"))
|
||||||
}
|
}
|
||||||
def parsePropertyDefinition(name: String)(value: String) = value.split("=",2) match {
|
def parsePropertyDefinition(name: String)(value: String) = value.split("=",2) match {
|
||||||
case Array(mode,value) => (mode, parsePropertyValue(name, value)(defineProperty(name)))
|
case Array(mode,value) => (mode, parsePropertyValue(name, value)(defineProperty(name)))
|
||||||
case x => throw new BootException("Invalid property definition '" + x + "' for property '" + name + "'")
|
case x => error("Invalid property definition '" + x + "' for property '" + name + "'")
|
||||||
}
|
}
|
||||||
def defineProperty(name: String)(action: String, requiredArg: String, optionalArg: Option[String]) =
|
def defineProperty(name: String)(action: String, requiredArg: String, optionalArg: Option[String]) =
|
||||||
action match
|
action match
|
||||||
{
|
{
|
||||||
case "prompt" => PromptProperty(requiredArg, optionalArg)
|
case "prompt" => new PromptProperty(requiredArg, optionalArg)
|
||||||
case "set" => SetProperty(requiredArg)
|
case "set" => new SetProperty(requiredArg)
|
||||||
case _ => throw new BootException("Unknown action '" + action + "' for property '" + name + "'")
|
case _ => error("Unknown action '" + action + "' for property '" + name + "'")
|
||||||
}
|
}
|
||||||
private lazy val propertyPattern = """(.+)\((.*)\)(?:\[(.*)\])?""".r.pattern // examples: prompt(Version)[1.0] or set(1.0)
|
private lazy val propertyPattern = Pattern.compile("""(.+)\((.*)\)(?:\[(.*)\])?""") // examples: prompt(Version)[1.0] or set(1.0)
|
||||||
def parsePropertyValue[T](name: String, definition: String)(f: (String, String, Option[String]) => T): T =
|
def parsePropertyValue[T](name: String, definition: String)(f: (String, String, Option[String]) => T): T =
|
||||||
{
|
{
|
||||||
val m = propertyPattern.matcher(definition)
|
val m = propertyPattern.matcher(definition)
|
||||||
if(!m.matches()) throw new BootException("Invalid property definition '" + definition + "' for property '" + name + "'")
|
if(!m.matches()) error("Invalid property definition '" + definition + "' for property '" + name + "'")
|
||||||
val optionalArg = m.group(3)
|
val optionalArg = m.group(3)
|
||||||
f(m.group(1), m.group(2), if(optionalArg eq null) None else Some(optionalArg))
|
f(m.group(1), m.group(2), if(optionalArg eq null) None else Some(optionalArg))
|
||||||
}
|
}
|
||||||
def trim(s: Array[String]) = s.map(_.trim)
|
def trim(s: Array[String]) = s.map(_.trim).toList
|
||||||
|
|
||||||
// line parsing
|
type LabelMap = ListMap[String, Option[String]]
|
||||||
|
|
||||||
def sections = lines ^^ processLines
|
|
||||||
type LabelMap = Map[String, Option[String]]
|
|
||||||
// section-name -> label -> value
|
// section-name -> label -> value
|
||||||
type SectionMap = Map[String, LabelMap]
|
type SectionMap = ListMap[String, LabelMap]
|
||||||
def processLines(lines: List[Line]): SectionMap =
|
def processLines(lines: List[Line]): SectionMap =
|
||||||
{
|
{
|
||||||
type State = (SectionMap, Option[String])
|
type State = (SectionMap, Option[String])
|
||||||
val s: State =
|
val s: State =
|
||||||
( ( (Map.empty withDefaultValue(TreeMap.empty[String,Option[String]]), None): State) /: lines ) {
|
( ( (ListMap.empty.default(x => ListMap.empty[String,Option[String]]), None): State) /: lines ) {
|
||||||
case (x, Comment) => x
|
case (x, Comment) => x
|
||||||
case ( (map, _), Section(name) ) => (map, Some(name))
|
case ( (map, _), s: Section ) => (map, Some(s.name))
|
||||||
case ( (_, None), l: Labeled ) => throw new BootException("Label " + l.label + " is not in a section")
|
case ( (_, None), l: Labeled ) => error("Label " + l.label + " is not in a section")
|
||||||
case ( (map, s @ Some(section)), l: Labeled ) =>
|
case ( (map, s @ Some(section)), l: Labeled ) =>
|
||||||
val sMap = map(section)
|
val sMap = map(section)
|
||||||
if( sMap.contains(l.label) ) throw new BootException("Duplicate label '" + l.label + "' in section '" + section + "'")
|
if( sMap.contains(l.label) ) error("Duplicate label '" + l.label + "' in section '" + section + "'")
|
||||||
else ( map(section) = (sMap(l.label) = l.value), s )
|
else ( map(section) = (sMap(l.label) = l.value), s )
|
||||||
}
|
}
|
||||||
s._1
|
s._1
|
||||||
}
|
}
|
||||||
|
|
||||||
// character parsing
|
|
||||||
type Elem = Char
|
|
||||||
def lines = (line*) <~ ws_nl
|
|
||||||
def line: Parser[Line] = (ws_nl ~> (comment | section | labeled | failure("Expected comment, start of section, or section entry")) ~! nl ) ^^ { case m ~ _ => m }
|
|
||||||
def comment = '#' ~! value ^^ { x => Comment }
|
|
||||||
def section = ('[' ~! ws) ~! ID ~! (ws ~! ']' ~! ws) ^^ { case _ ~ i ~ _ => Section(i)}
|
|
||||||
def labeled = ID ~! ws ~! ((':' ~! value ^^ { case _ ~ v => v })?) >> {
|
|
||||||
case k ~ _ ~ Some(v) =>
|
|
||||||
val trimmed = v.trim
|
|
||||||
if(trimmed.isEmpty) failure("Value for '" + k + "' was empty") else success(Labeled(k, Some(v.trim)))
|
|
||||||
case k ~ _ ~ None => success(Labeled(k, None))
|
|
||||||
}
|
|
||||||
|
|
||||||
def ws_nl = string(isWhitespace)
|
|
||||||
lazy val ws = string(c => isWhitespace(c) && !isNewline(c))
|
|
||||||
|
|
||||||
def ID = IDword ~! ((ws ~> IDword)*) ^^ { case (x ~ y) => (x :: y).mkString(" ") }
|
|
||||||
def IDword = elem("Identifier", isIDStart) ~! string(isIDChar) ^^ { case x ~ xs => x + xs }
|
|
||||||
def value = string(c => !isNewline(c))
|
|
||||||
|
|
||||||
def isNewline(c: Char) = c == '\r' || c == '\n'
|
|
||||||
def isIDStart(c: Char) = isIDChar(c) && c != '[' && c != '#'
|
|
||||||
def isIDChar(c: Char) = !isWhitespace(c) && c != ':' && c != ']' && c != CharSequenceReader.EofCh
|
|
||||||
|
|
||||||
case class string(accept: Char => Boolean) extends Parser[String] with NotNull
|
|
||||||
{
|
|
||||||
def apply(in: Reader[Char]) =
|
|
||||||
{
|
|
||||||
val buffer = new StringBuilder
|
|
||||||
def fill(in: Reader[Char]): ParseResult[String] =
|
|
||||||
{
|
|
||||||
if(in.atEnd || !accept(in.first))
|
|
||||||
Success(buffer.toString, in)
|
|
||||||
else
|
|
||||||
{
|
|
||||||
buffer += in.first
|
|
||||||
fill(in.rest)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
fill(in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
def nl = new Parser[Unit] with NotNull
|
|
||||||
{
|
|
||||||
def apply(in: Reader[Char]) =
|
|
||||||
{
|
|
||||||
if(in.atEnd) Success( (), in)
|
|
||||||
else if(isNewline(in.first)) Success( (), in.rest )
|
|
||||||
else Failure("Expected end of line", in)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait Line extends NotNull
|
sealed trait Line extends NotNull
|
||||||
final case class Labeled(label: String, value: Option[String]) extends Line
|
final class Labeled(val label: String, val value: Option[String]) extends Line
|
||||||
final case class Section(name: String) extends Line
|
final class Section(val name: String) extends Line
|
||||||
object Comment extends Line
|
object Comment extends Line
|
||||||
|
|
||||||
|
class ParseException(val content: String, val line: Int, val col: Int, val msg: String)
|
||||||
|
extends BootException( "[" + (line+1) + ", " + (col+1) + "]" + msg + "\n" + content + "\n" + List.make(col," ").mkString + "^" )
|
||||||
|
|
||||||
|
object ParseLine
|
||||||
|
{
|
||||||
|
def apply(content: String, line: Int) =
|
||||||
|
{
|
||||||
|
def error(col: Int, msg: String) = throw new ParseException(content, line, col, msg)
|
||||||
|
def check(condition: Boolean)(col: Int, msg: String) = if(condition) () else error(col, msg)
|
||||||
|
|
||||||
|
val trimmed = trimLeading(content)
|
||||||
|
val offset = content.length - trimmed.length
|
||||||
|
|
||||||
|
def section =
|
||||||
|
{
|
||||||
|
val closing = trimmed.indexOf(']', 1)
|
||||||
|
check(closing > 0)(content.length, "Expected ']', found end of line")
|
||||||
|
val extra = trimmed.substring(closing+1)
|
||||||
|
val trimmedExtra = trimLeading(extra)
|
||||||
|
check(isEmpty(trimmedExtra))(content.length - trimmedExtra.length, "Expected end of line, found '" + extra + "'")
|
||||||
|
new Section(trimmed.substring(1,closing).trim)
|
||||||
|
}
|
||||||
|
def labeled =
|
||||||
|
{
|
||||||
|
trimmed.split(":",2) match {
|
||||||
|
case Array(label, value) =>
|
||||||
|
val trimmedValue = value.trim
|
||||||
|
check(isNonEmpty(trimmedValue))(content.indexOf(':'), "Value for '" + label + "' was empty")
|
||||||
|
new Labeled(label, Some(trimmedValue))
|
||||||
|
case x => new Labeled(x.mkString, None)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if(isEmpty(trimmed)) Nil
|
||||||
|
else
|
||||||
|
{
|
||||||
|
val processed =
|
||||||
|
trimmed.charAt(0) match
|
||||||
|
{
|
||||||
|
case '#' => Comment
|
||||||
|
case '[' => section
|
||||||
|
case _ => labeled
|
||||||
|
}
|
||||||
|
processed :: Nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,26 +1,27 @@
|
||||||
package xsbt.boot
|
package xsbt.boot
|
||||||
|
|
||||||
|
import Pre._
|
||||||
import java.io.{File, FileInputStream, FileOutputStream}
|
import java.io.{File, FileInputStream, FileOutputStream}
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
||||||
object Initialize
|
object Initialize
|
||||||
{
|
{
|
||||||
def create(file: File, promptCreate: String, enableQuick: Boolean, spec: Seq[AppProperty])
|
def create(file: File, promptCreate: String, enableQuick: Boolean, spec: List[AppProperty])
|
||||||
{
|
{
|
||||||
SimpleReader.readLine(promptCreate + " (y/N" + (if(enableQuick) "/s" else "") + ") ") match
|
SimpleReader.readLine(promptCreate + " (y/N" + (if(enableQuick) "/s" else "") + ") ") match
|
||||||
{
|
{
|
||||||
case None => throw new BootException("")
|
case None => error("")
|
||||||
case Some(line) =>
|
case Some(line) =>
|
||||||
line.toLowerCase match
|
line.toLowerCase match
|
||||||
{
|
{
|
||||||
case "y" | "yes" => process(file, spec, _.create)
|
case "y" | "yes" => process(file, spec, _.create)
|
||||||
case "n" | "no" | "" => throw new BootException("")
|
case "n" | "no" | "" => error("")
|
||||||
case "s" => process(file, spec, _.quick)
|
case "s" => process(file, spec, _.quick)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def fill(file: File, spec: Seq[AppProperty]): Unit = process(file, spec, _.fill)
|
def fill(file: File, spec: List[AppProperty]): Unit = process(file, spec, _.fill)
|
||||||
def process(file: File, appProperties: Seq[AppProperty], select: AppProperty => Option[PropertyInit])
|
def process(file: File, appProperties: List[AppProperty], select: AppProperty => Option[PropertyInit])
|
||||||
{
|
{
|
||||||
val properties = new Properties
|
val properties = new Properties
|
||||||
if(file.exists)
|
if(file.exists)
|
||||||
|
|
@ -34,14 +35,14 @@ object Initialize
|
||||||
{
|
{
|
||||||
init match
|
init match
|
||||||
{
|
{
|
||||||
case SetProperty(value) => properties.setProperty(name, value)
|
case set: SetProperty => properties.setProperty(name, set.value)
|
||||||
case PromptProperty(label, default) =>
|
case prompt: PromptProperty =>
|
||||||
def noValue = throw new BootException("No value provided for " + label)
|
def noValue = error("No value provided for " + prompt.label)
|
||||||
SimpleReader.readLine(label + default.toList.map(" [" + _ + "]").mkString + ": ") match
|
SimpleReader.readLine(prompt.label + prompt.default.toList.map(" [" + _ + "]").mkString + ": ") match
|
||||||
{
|
{
|
||||||
case None => noValue
|
case None => noValue
|
||||||
case Some(line) =>
|
case Some(line) =>
|
||||||
val value = if(line.isEmpty) default.getOrElse(noValue) else line
|
val value = if(isEmpty(line)) prompt.default.getOrElse(noValue) else line
|
||||||
properties.setProperty(name, value)
|
properties.setProperty(name, value)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
/* sbt -- Simple Build Tool
|
||||||
|
* Copyright 2008,2009 David MacIver, Mark Harrah
|
||||||
|
*/
|
||||||
|
package xsbt.boot
|
||||||
|
|
||||||
|
import Pre._
|
||||||
|
|
||||||
|
class Enumeration extends NotNull
|
||||||
|
{
|
||||||
|
def elements: List[Value] = members
|
||||||
|
private lazy val members: List[Value] =
|
||||||
|
{
|
||||||
|
val c = getClass
|
||||||
|
val correspondingFields = ListMap( c.getDeclaredFields.map(f => (f.getName, f)) : _*)
|
||||||
|
c.getMethods.toList flatMap { method =>
|
||||||
|
if(method.getParameterTypes.length == 0 && classOf[Value].isAssignableFrom(method.getReturnType))
|
||||||
|
{
|
||||||
|
for(field <- correspondingFields.get(method.getName) if field.getType == method.getReturnType) yield
|
||||||
|
method.invoke(this).asInstanceOf[Value]
|
||||||
|
}
|
||||||
|
else
|
||||||
|
Nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
def value(s: String) = new Value(s)
|
||||||
|
class Value(override val toString: String) extends NotNull
|
||||||
|
|
||||||
|
def toValue(s: String): Value = elements.find(_.toString == s).getOrElse(error("Expected one of " + elements.mkString(",") + " (got: " + s + ")"))
|
||||||
|
}
|
||||||
|
|
@ -1,5 +1,6 @@
|
||||||
package xsbt.boot
|
package xsbt.boot
|
||||||
|
|
||||||
|
import Pre._
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URI
|
import java.net.URI
|
||||||
|
|
||||||
|
|
|
||||||
|
|
@ -1,64 +1,79 @@
|
||||||
package xsbt.boot
|
package xsbt.boot
|
||||||
|
|
||||||
|
import Pre._
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
object Launch
|
object Launch
|
||||||
{
|
{
|
||||||
def apply(arguments: Seq[String]): Unit = apply( (new File("")).getAbsoluteFile , arguments )
|
val start = System.currentTimeMillis
|
||||||
|
def time(label: String) = System.out.println(label + " : " + (System.currentTimeMillis - start) / 1000.0 + " s")
|
||||||
|
def apply(arguments: List[String]): Unit = apply( (new File("")).getAbsoluteFile , arguments )
|
||||||
|
|
||||||
def apply(currentDirectory: File, arguments: Seq[String]): Unit =
|
def apply(currentDirectory: File, arguments: List[String]): Unit =
|
||||||
Configuration.find(arguments, currentDirectory) match { case (configLocation, newArguments) => configured(currentDirectory, configLocation, newArguments) }
|
Configuration.find(arguments, currentDirectory) match { case (configLocation, newArguments) => configured(currentDirectory, configLocation, newArguments) }
|
||||||
|
|
||||||
def configured(currentDirectory: File, configLocation: URL, arguments: Seq[String]): Unit =
|
def configured(currentDirectory: File, configLocation: URL, arguments: List[String]): Unit =
|
||||||
{
|
{
|
||||||
|
time("found boot config")
|
||||||
val config = Configuration.parse(configLocation, currentDirectory)
|
val config = Configuration.parse(configLocation, currentDirectory)
|
||||||
|
time("parsed")
|
||||||
Find(config, currentDirectory) match { case (resolved, baseDirectory) => parsed(baseDirectory, resolved, arguments) }
|
Find(config, currentDirectory) match { case (resolved, baseDirectory) => parsed(baseDirectory, resolved, arguments) }
|
||||||
}
|
}
|
||||||
def parsed(currentDirectory: File, parsed: LaunchConfiguration, arguments: Seq[String]): Unit =
|
def parsed(currentDirectory: File, parsed: LaunchConfiguration, arguments: List[String]): Unit =
|
||||||
{
|
{
|
||||||
|
time("found working directory")
|
||||||
val propertiesFile = parsed.boot.properties
|
val propertiesFile = parsed.boot.properties
|
||||||
import parsed.boot.{enableQuick, promptCreate, promptFill}
|
import parsed.boot.{enableQuick, promptCreate, promptFill}
|
||||||
if(!promptCreate.isEmpty && !propertiesFile.exists)
|
if(isNonEmpty(promptCreate) && !propertiesFile.exists)
|
||||||
Initialize.create(propertiesFile, promptCreate, enableQuick, parsed.appProperties)
|
Initialize.create(propertiesFile, promptCreate, enableQuick, parsed.appProperties)
|
||||||
else if(promptFill)
|
else if(promptFill)
|
||||||
Initialize.fill(propertiesFile, parsed.appProperties)
|
Initialize.fill(propertiesFile, parsed.appProperties)
|
||||||
|
time("initialized")
|
||||||
initialized(currentDirectory, parsed, arguments)
|
initialized(currentDirectory, parsed, arguments)
|
||||||
}
|
}
|
||||||
def initialized(currentDirectory: File, parsed: LaunchConfiguration, arguments: Seq[String]): Unit =
|
def initialized(currentDirectory: File, parsed: LaunchConfiguration, arguments: List[String]): Unit =
|
||||||
explicit(currentDirectory, ResolveVersions(parsed), arguments)
|
{
|
||||||
|
val resolved = ResolveVersions(parsed)
|
||||||
|
time("resolved")
|
||||||
|
explicit(currentDirectory, resolved, arguments)
|
||||||
|
}
|
||||||
|
|
||||||
def explicit(currentDirectory: File, explicit: LaunchConfiguration, arguments: Seq[String]): Unit =
|
def explicit(currentDirectory: File, explicit: LaunchConfiguration, arguments: List[String]): Unit =
|
||||||
launch( run(new Launch(explicit.boot.directory, explicit.repositories)) ) (
|
launch( run(new Launch(explicit.boot.directory, explicit.repositories)) ) (
|
||||||
RunConfiguration(explicit.getScalaVersion, explicit.app.toID, currentDirectory, arguments) )
|
new RunConfiguration(explicit.getScalaVersion, explicit.app.toID, currentDirectory, arguments) )
|
||||||
|
|
||||||
def run(launcher: xsbti.Launcher)(config: RunConfiguration): xsbti.MainResult =
|
def run(launcher: xsbti.Launcher)(config: RunConfiguration): xsbti.MainResult =
|
||||||
{
|
{
|
||||||
import config._
|
import config._
|
||||||
val scalaProvider: xsbti.ScalaProvider = launcher.getScala(scalaVersion)
|
val scalaProvider: xsbti.ScalaProvider = launcher.getScala(scalaVersion)
|
||||||
val appProvider: xsbti.AppProvider = scalaProvider.app(app)
|
val appProvider: xsbti.AppProvider = scalaProvider.app(app)
|
||||||
val appConfig: xsbti.AppConfiguration = new AppConfiguration(arguments.toArray, workingDirectory, appProvider)
|
val appConfig: xsbti.AppConfiguration = new AppConfiguration(toArray(arguments), workingDirectory, appProvider)
|
||||||
|
|
||||||
appProvider.newMain().run(appConfig)
|
time("pre-load")
|
||||||
|
val main = appProvider.newMain()
|
||||||
|
time("loaded")
|
||||||
|
val result = main.run(appConfig)
|
||||||
|
time("ran")
|
||||||
|
result
|
||||||
}
|
}
|
||||||
final def launch(run: RunConfiguration => xsbti.MainResult)(config: RunConfiguration)
|
final def launch(run: RunConfiguration => xsbti.MainResult)(config: RunConfiguration)
|
||||||
{
|
{
|
||||||
run(config) match
|
run(config) match
|
||||||
{
|
{
|
||||||
case e: xsbti.Exit => System.exit(e.code)
|
case e: xsbti.Exit => System.exit(e.code)
|
||||||
case r: xsbti.Reboot => launch(run)(RunConfiguration(r.scalaVersion, r.app, r.baseDirectory, r.arguments))
|
case r: xsbti.Reboot => launch(run)(new RunConfiguration(r.scalaVersion, r.app, r.baseDirectory, r.arguments.toList))
|
||||||
case x => throw new BootException("Invalid main result: " + x + (if(x eq null) "" else " (class: " + x.getClass + ")"))
|
case x => throw new BootException("Invalid main result: " + x + (if(x eq null) "" else " (class: " + x.getClass + ")"))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case class RunConfiguration(scalaVersion: String, app: xsbti.ApplicationID, workingDirectory: File, arguments: Seq[String]) extends NotNull
|
final class RunConfiguration(val scalaVersion: String, val app: xsbti.ApplicationID, val workingDirectory: File, val arguments: List[String]) extends NotNull
|
||||||
|
|
||||||
import BootConfiguration.{appDirectoryName, baseDirectoryName, ScalaDirectoryName, TestLoadScalaClasses}
|
import BootConfiguration.{appDirectoryName, baseDirectoryName, ScalaDirectoryName, TestLoadScalaClasses}
|
||||||
class Launch(val bootDirectory: File, repositories: Seq[Repository]) extends xsbti.Launcher
|
class Launch(val bootDirectory: File, repositories: List[Repository]) extends xsbti.Launcher
|
||||||
{
|
{
|
||||||
import scala.collection.mutable.HashMap
|
private val scalaProviders = new Cache[String, ScalaProvider](new ScalaProvider(_))
|
||||||
private val scalaProviders = new HashMap[String, ScalaProvider]
|
def getScala(version: String): xsbti.ScalaProvider = scalaProviders(version)
|
||||||
def getScala(version: String): xsbti.ScalaProvider = scalaProviders.getOrElseUpdate(version, new ScalaProvider(version))
|
|
||||||
|
|
||||||
lazy val topLoader = new BootFilteredLoader(getClass.getClassLoader)
|
lazy val topLoader = new BootFilteredLoader(getClass.getClassLoader)
|
||||||
|
|
||||||
|
|
@ -74,7 +89,7 @@ class Launch(val bootDirectory: File, repositories: Seq[Repository]) extends xsb
|
||||||
lazy val scalaHome = new File(libDirectory, ScalaDirectoryName)
|
lazy val scalaHome = new File(libDirectory, ScalaDirectoryName)
|
||||||
def compilerJar = new File(scalaHome, "scala-compiler.jar")
|
def compilerJar = new File(scalaHome, "scala-compiler.jar")
|
||||||
def libraryJar = new File(scalaHome, "scala-library.jar")
|
def libraryJar = new File(scalaHome, "scala-library.jar")
|
||||||
def baseDirectories = Seq(scalaHome)
|
def baseDirectories = List(scalaHome)
|
||||||
def testLoadClasses = TestLoadScalaClasses
|
def testLoadClasses = TestLoadScalaClasses
|
||||||
def target = UpdateScala
|
def target = UpdateScala
|
||||||
def failLabel = "Scala " + version
|
def failLabel = "Scala " + version
|
||||||
|
|
@ -87,8 +102,8 @@ class Launch(val bootDirectory: File, repositories: Seq[Repository]) extends xsb
|
||||||
def configuration = ScalaProvider.this.configuration
|
def configuration = ScalaProvider.this.configuration
|
||||||
lazy val appHome = new File(libDirectory, appDirectoryName(id, File.separator))
|
lazy val appHome = new File(libDirectory, appDirectoryName(id, File.separator))
|
||||||
def parentLoader = ScalaProvider.this.loader
|
def parentLoader = ScalaProvider.this.loader
|
||||||
def baseDirectories = id.mainComponents.map(components.componentLocation) ++ Seq(appHome)
|
def baseDirectories = appHome :: id.mainComponents.map(components.componentLocation).toList
|
||||||
def testLoadClasses = Seq(id.mainClass)
|
def testLoadClasses = List(id.mainClass)
|
||||||
def target = new UpdateApp(Application(id))
|
def target = new UpdateApp(Application(id))
|
||||||
def failLabel = id.name + " " + id.version
|
def failLabel = id.name + " " + id.version
|
||||||
|
|
||||||
|
|
@ -113,7 +128,7 @@ class ComponentProvider(baseDirectory: File) extends xsbti.ComponentProvider
|
||||||
if(location.exists)
|
if(location.exists)
|
||||||
throw new BootException("Cannot redefine component. ID: " + id + ", files: " + files.mkString(","))
|
throw new BootException("Cannot redefine component. ID: " + id + ", files: " + files.mkString(","))
|
||||||
else
|
else
|
||||||
Copy(files, location)
|
Copy(files.toList, location)
|
||||||
}
|
}
|
||||||
def lockFile = ComponentProvider.lockFile(baseDirectory)
|
def lockFile = ComponentProvider.lockFile(baseDirectory)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -1,47 +1,42 @@
|
||||||
package xsbt.boot
|
package xsbt.boot
|
||||||
|
|
||||||
|
import Pre._
|
||||||
import java.io.File
|
import java.io.File
|
||||||
import java.net.URL
|
import java.net.URL
|
||||||
|
|
||||||
final case class LaunchConfiguration(scalaVersion: Version, app: Application, repositories: Seq[Repository], boot: BootSetup, logging: Logging, appProperties: Seq[AppProperty]) extends NotNull
|
final case class LaunchConfiguration(scalaVersion: Version, app: Application, repositories: List[Repository], boot: BootSetup, logging: Logging, appProperties: List[AppProperty]) extends NotNull
|
||||||
{
|
{
|
||||||
def getScalaVersion = Version.get(scalaVersion)
|
def getScalaVersion = Version.get(scalaVersion)
|
||||||
def withScalaVersion(newScalaVersion: String) = LaunchConfiguration(Version.Explicit(newScalaVersion), app, repositories, boot, logging, appProperties)
|
def withScalaVersion(newScalaVersion: String) = LaunchConfiguration(new Version.Explicit(newScalaVersion), app, repositories, boot, logging, appProperties)
|
||||||
def withApp(app: Application) = LaunchConfiguration(scalaVersion, app, repositories, boot, logging, appProperties)
|
def withApp(app: Application) = LaunchConfiguration(scalaVersion, app, repositories, boot, logging, appProperties)
|
||||||
def withAppVersion(newAppVersion: String) = LaunchConfiguration(scalaVersion, app.withVersion(Version.Explicit(newAppVersion)), repositories, boot, logging, appProperties)
|
def withAppVersion(newAppVersion: String) = LaunchConfiguration(scalaVersion, app.withVersion(new Version.Explicit(newAppVersion)), repositories, boot, logging, appProperties)
|
||||||
def withVersions(newScalaVersion: String, newAppVersion: String) = LaunchConfiguration(Version.Explicit(newScalaVersion), app.withVersion(Version.Explicit(newAppVersion)), repositories, boot, logging, appProperties)
|
def withVersions(newScalaVersion: String, newAppVersion: String) = LaunchConfiguration(new Version.Explicit(newScalaVersion), app.withVersion(new Version.Explicit(newAppVersion)), repositories, boot, logging, appProperties)
|
||||||
def map(f: File => File) = LaunchConfiguration(scalaVersion, app, repositories, boot.map(f), logging, appProperties)
|
def map(f: File => File) = LaunchConfiguration(scalaVersion, app, repositories, boot.map(f), logging, appProperties)
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed trait Version extends NotNull
|
sealed trait Version extends NotNull
|
||||||
object Version
|
object Version
|
||||||
{
|
{
|
||||||
final case class Explicit(value: String) extends Version
|
final class Explicit(val value: String) extends Version
|
||||||
final case class Implicit(default: Option[String]) extends Version
|
final class Implicit(val default: Option[String]) extends Version
|
||||||
{
|
{
|
||||||
require(default.isEmpty || !default.get.isEmpty, "Default cannot be empty")
|
require(default.isEmpty || isNonEmpty(default.get), "Default cannot be the empty string")
|
||||||
}
|
}
|
||||||
|
|
||||||
object Implicit
|
object Implicit
|
||||||
{
|
{
|
||||||
def apply(s: String, default: Option[String]): Either[String, Implicit] =
|
def apply(s: String, default: Option[String])(handleError: String => Version): Version =
|
||||||
if(s == "read") Right(Implicit(default)) else Left("Expected 'read', got '" + s +"'")
|
if(s == "read") new Implicit(default) else handleError("Expected 'read', got '" + s +"'")
|
||||||
}
|
}
|
||||||
def get(v: Version) = v match { case Version.Explicit(v) => v; case _ => throw new BootException("Unresolved version: " + v) }
|
def get(v: Version) = v match { case e: Version.Explicit => e.value; case _ => throw new BootException("Unresolved version: " + v) }
|
||||||
def default = Implicit(None)
|
def default = new Implicit(None)
|
||||||
}
|
}
|
||||||
|
|
||||||
sealed abstract class RichEnum extends Enumeration
|
final case class Application(groupID: String, name: String, version: Version, main: String, components: List[String], crossVersioned: Boolean) extends NotNull
|
||||||
{
|
|
||||||
def fromString(s: String): Either[String, Value] = elements.find(_.toString == s).toRight("Expected one of " + elements.mkString(",") + " (got: " + s + ")")
|
|
||||||
def toValue(s: String): Value = fromString(s) match { case Left(msg) => error(msg); case Right(t) => t }
|
|
||||||
}
|
|
||||||
|
|
||||||
final case class Application(groupID: String, name: String, version: Version, main: String, components: Seq[String], crossVersioned: Boolean) extends NotNull
|
|
||||||
{
|
{
|
||||||
def getVersion = Version.get(version)
|
def getVersion = Version.get(version)
|
||||||
def withVersion(newVersion: Version) = Application(groupID, name, newVersion, main, components, crossVersioned)
|
def withVersion(newVersion: Version) = Application(groupID, name, newVersion, main, components, crossVersioned)
|
||||||
def toID = AppID(groupID, name, getVersion, main, components.toArray, crossVersioned)
|
def toID = AppID(groupID, name, getVersion, main, toArray(components), crossVersioned)
|
||||||
}
|
}
|
||||||
final case class AppID(groupID: String, name: String, version: String, mainClass: String, mainComponents: Array[String], crossVersioned: Boolean) extends xsbti.ApplicationID
|
final case class AppID(groupID: String, name: String, version: String, mainClass: String, mainComponents: Array[String], crossVersioned: Boolean) extends xsbti.ApplicationID
|
||||||
|
|
||||||
|
|
@ -50,7 +45,7 @@ object Application
|
||||||
def apply(id: xsbti.ApplicationID): Application =
|
def apply(id: xsbti.ApplicationID): Application =
|
||||||
{
|
{
|
||||||
import id._
|
import id._
|
||||||
Application(groupID, name, Version.Explicit(version), mainClass, mainComponents, crossVersioned)
|
Application(groupID, name, new Version.Explicit(version), mainClass, mainComponents.toList, crossVersioned)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
@ -61,28 +56,28 @@ object Repository
|
||||||
final case class Ivy(id: String, url: URL, pattern: String) extends Repository
|
final case class Ivy(id: String, url: URL, pattern: String) extends Repository
|
||||||
final case class Predefined(id: Predefined.Value) extends Repository
|
final case class Predefined(id: Predefined.Value) extends Repository
|
||||||
|
|
||||||
object Predefined extends RichEnum
|
object Predefined extends Enumeration
|
||||||
{
|
{
|
||||||
val Local = Value("local")
|
val Local = value("local")
|
||||||
val MavenLocal = Value("maven-local")
|
val MavenLocal = value("maven-local")
|
||||||
val MavenCentral = Value("maven-central")
|
val MavenCentral = value("maven-central")
|
||||||
val ScalaToolsReleases = Value("scala-tools-releases")
|
val ScalaToolsReleases = value("scala-tools-releases")
|
||||||
val ScalaToolsSnapshots = Value("scala-tools-snapshots")
|
val ScalaToolsSnapshots = value("scala-tools-snapshots")
|
||||||
def apply(s: String): Predefined = Predefined(toValue(s))
|
def apply(s: String): Predefined = Predefined(toValue(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
def defaults: Seq[Repository] = Predefined.elements.map(Predefined.apply).toList
|
def defaults: List[Repository] = Predefined.elements.map(Predefined.apply).toList
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class Search(tpe: Search.Value, paths: Seq[File]) extends NotNull
|
final case class Search(tpe: Search.Value, paths: List[File]) extends NotNull
|
||||||
object Search extends RichEnum
|
object Search extends Enumeration
|
||||||
{
|
{
|
||||||
def none = Search(Current, Nil)
|
def none = Search(Current, Nil)
|
||||||
val Only = Value("only")
|
val Only = value("only")
|
||||||
val RootFirst = Value("root-first")
|
val RootFirst = value("root-first")
|
||||||
val Nearest = Value("nearest")
|
val Nearest = value("nearest")
|
||||||
val Current = Value("none")
|
val Current = value("none")
|
||||||
def apply(s: String, paths: Seq[File]): Search = Search(toValue(s), paths)
|
def apply(s: String, paths: List[File]): Search = Search(toValue(s), paths)
|
||||||
}
|
}
|
||||||
|
|
||||||
final case class BootSetup(directory: File, properties: File, search: Search, promptCreate: String, enableQuick: Boolean, promptFill: Boolean) extends NotNull
|
final case class BootSetup(directory: File, properties: File, search: Search, promptCreate: String, enableQuick: Boolean, promptFill: Boolean) extends NotNull
|
||||||
|
|
@ -92,21 +87,21 @@ final case class BootSetup(directory: File, properties: File, search: Search, pr
|
||||||
final case class AppProperty(name: String)(val quick: Option[PropertyInit], val create: Option[PropertyInit], val fill: Option[PropertyInit]) extends NotNull
|
final case class AppProperty(name: String)(val quick: Option[PropertyInit], val create: Option[PropertyInit], val fill: Option[PropertyInit]) extends NotNull
|
||||||
|
|
||||||
sealed trait PropertyInit extends NotNull
|
sealed trait PropertyInit extends NotNull
|
||||||
final case class SetProperty(value: String) extends PropertyInit
|
final class SetProperty(val value: String) extends PropertyInit
|
||||||
final case class PromptProperty(label: String, default: Option[String]) extends PropertyInit
|
final class PromptProperty(val label: String, val default: Option[String]) extends PropertyInit
|
||||||
|
|
||||||
final case class Logging(level: LogLevel.Value) extends NotNull
|
final class Logging(level: LogLevel.Value) extends NotNull
|
||||||
object LogLevel extends RichEnum
|
object LogLevel extends Enumeration
|
||||||
{
|
{
|
||||||
val Debug = Value("debug")
|
val Debug = value("debug")
|
||||||
val Info = Value("info")
|
val Info = value("info")
|
||||||
val Warn = Value("warn")
|
val Warn = value("warn")
|
||||||
val Error = Value("error")
|
val Error = value("error")
|
||||||
def apply(s: String): Logging = Logging(toValue(s))
|
def apply(s: String): Logging = new Logging(toValue(s))
|
||||||
}
|
}
|
||||||
|
|
||||||
final class AppConfiguration(val arguments: Array[String], val baseDirectory: File, val provider: xsbti.AppProvider) extends xsbti.AppConfiguration
|
final class AppConfiguration(val arguments: Array[String], val baseDirectory: File, val provider: xsbti.AppProvider) extends xsbti.AppConfiguration
|
||||||
// The exception to use when an error occurs at the launcher level (and not a nested exception).
|
// The exception to use when an error occurs at the launcher level (and not a nested exception).
|
||||||
// This indicates overrides toString because the exception class name is not needed to understand
|
// This indicates overrides toString because the exception class name is not needed to understand
|
||||||
// the error message.
|
// the error message.
|
||||||
final class BootException(override val toString: String) extends RuntimeException
|
class BootException(override val toString: String) extends RuntimeException
|
||||||
|
|
@ -0,0 +1,32 @@
|
||||||
|
package xsbt.boot
|
||||||
|
|
||||||
|
import Pre._
|
||||||
|
|
||||||
|
// preserves iteration order
|
||||||
|
sealed class ListMap[K,V] private(backing: List[(K,V)]) extends Iterable[(K,V)] with NotNull
|
||||||
|
{
|
||||||
|
import ListMap.remove
|
||||||
|
def update(k: K, v: V) = this.+( (k,v) )
|
||||||
|
def +(pair: (K,V)) = copy(pair :: remove(backing,pair._1))
|
||||||
|
def -(k: K) = copy(remove(backing,k))
|
||||||
|
def get(k: K): Option[V] = backing.find(_._1 == k).map(_._2)
|
||||||
|
def keys: List[K] = backing.reverse.map(_._1)
|
||||||
|
def apply(k: K): V = get(k).getOrElse(error("Key " + k + " not found"))
|
||||||
|
def contains(k: K): Boolean = get(k).isDefined
|
||||||
|
def elements: Iterator[(K,V)] = backing.reverse.elements
|
||||||
|
override def isEmpty: Boolean = backing.isEmpty
|
||||||
|
override def toList = backing.reverse
|
||||||
|
override def toSeq = toList
|
||||||
|
protected def copy(newBacking: List[(K,V)]): ListMap[K,V] = new ListMap(newBacking)
|
||||||
|
def default(defaultF: K => V): ListMap[K,V] =
|
||||||
|
new ListMap[K,V](backing) {
|
||||||
|
override def apply(k: K) = super.get(k).getOrElse(defaultF(k))
|
||||||
|
override def copy(newBacking: List[(K,V)]) = super.copy(newBacking).default(defaultF)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
object ListMap
|
||||||
|
{
|
||||||
|
def apply[K,V](pairs: (K,V)*) = (empty[K,V] /: pairs)(_ + _)
|
||||||
|
def empty[K,V] = new ListMap[K,V](Nil)
|
||||||
|
private def remove[K,V](backing: List[(K,V)], k: K) = backing.filter(_._1 != k)
|
||||||
|
}
|
||||||
|
|
@ -7,13 +7,9 @@ import java.util.concurrent.Callable
|
||||||
// gets a file lock by first getting a JVM-wide lock.
|
// gets a file lock by first getting a JVM-wide lock.
|
||||||
object Locks extends xsbti.GlobalLock
|
object Locks extends xsbti.GlobalLock
|
||||||
{
|
{
|
||||||
import scala.collection.mutable.HashMap
|
private[this] val locks = new Cache[File, GlobalLock](new GlobalLock(_))
|
||||||
private[this] val locks = new HashMap[File, GlobalLock]
|
|
||||||
def apply[T](file: File, action: Callable[T]) =
|
def apply[T](file: File, action: Callable[T]) =
|
||||||
{
|
synchronized { locks(file.getCanonicalFile).withLock(action) }
|
||||||
val canonFile = file.getCanonicalFile
|
|
||||||
synchronized { locks.getOrElseUpdate(canonFile, new GlobalLock(canonFile)).withLock(action) }
|
|
||||||
}
|
|
||||||
|
|
||||||
private[this] class GlobalLock(file: File)
|
private[this] class GlobalLock(file: File)
|
||||||
{
|
{
|
||||||
|
|
@ -37,7 +33,7 @@ object Locks extends xsbti.GlobalLock
|
||||||
val freeLock = channel.tryLock
|
val freeLock = channel.tryLock
|
||||||
if(freeLock eq null)
|
if(freeLock eq null)
|
||||||
{
|
{
|
||||||
println("Waiting for lock on " + file + " to be available...");
|
System.out.println("Waiting for lock on " + file + " to be available...");
|
||||||
val lock = channel.lock
|
val lock = channel.lock
|
||||||
try { run.call }
|
try { run.call }
|
||||||
finally { lock.release() }
|
finally { lock.release() }
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,29 @@
|
||||||
|
package xsbt.boot
|
||||||
|
|
||||||
|
object Pre
|
||||||
|
{
|
||||||
|
def trimLeading(line: String) =
|
||||||
|
{
|
||||||
|
def newStart(i: Int): Int = if(i >= line.length || !Character.isWhitespace(line.charAt(i))) i else newStart(i+1)
|
||||||
|
line.substring(newStart(0))
|
||||||
|
}
|
||||||
|
def isEmpty(line: String) = line.length == 0
|
||||||
|
def isNonEmpty(line: String) = line.length > 0
|
||||||
|
def assert(condition: Boolean, msg: => String): Unit = if (!condition) throw new AssertionError(msg)
|
||||||
|
def assert(condition: Boolean): Unit = assert(condition, "Assertion failed")
|
||||||
|
def require(condition: Boolean, msg: => String): Unit = if (!condition) throw new IllegalArgumentException(msg)
|
||||||
|
def error(msg: String): Nothing = throw new BootException(msg)
|
||||||
|
def toBoolean(s: String) = java.lang.Boolean.parseBoolean(s)
|
||||||
|
def toArray[T](list: List[T]) =
|
||||||
|
{
|
||||||
|
val arr = new Array[T](list.length)
|
||||||
|
def copy(i: Int, rem: List[T]): Unit =
|
||||||
|
if(i < arr.length)
|
||||||
|
{
|
||||||
|
arr(i) = rem.head
|
||||||
|
copy(i+1, rem.tail)
|
||||||
|
}
|
||||||
|
copy(0, list)
|
||||||
|
arr
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -1,13 +1,14 @@
|
||||||
package xsbt.boot
|
package xsbt.boot
|
||||||
|
|
||||||
|
import Pre._
|
||||||
import java.io.{File, FileFilter}
|
import java.io.{File, FileFilter}
|
||||||
import java.net.{URL, URLClassLoader}
|
import java.net.{URL, URLClassLoader}
|
||||||
|
|
||||||
trait Provider extends NotNull
|
trait Provider extends NotNull
|
||||||
{
|
{
|
||||||
def configuration: UpdateConfiguration
|
def configuration: UpdateConfiguration
|
||||||
def baseDirectories: Seq[File]
|
def baseDirectories: List[File]
|
||||||
def testLoadClasses: Seq[String]
|
def testLoadClasses: List[String]
|
||||||
def target: UpdateTarget
|
def target: UpdateTarget
|
||||||
def failLabel: String
|
def failLabel: String
|
||||||
def parentLoader: ClassLoader
|
def parentLoader: ClassLoader
|
||||||
|
|
@ -28,13 +29,13 @@ trait Provider extends NotNull
|
||||||
def createLoader =
|
def createLoader =
|
||||||
{
|
{
|
||||||
val jars = GetJars(baseDirectories)
|
val jars = GetJars(baseDirectories)
|
||||||
(jars, new URLClassLoader(jars.map(_.toURI.toURL).toArray, parentLoader) )
|
(jars, new URLClassLoader(jars.map(_.toURI.toURL), parentLoader) )
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object GetJars
|
object GetJars
|
||||||
{
|
{
|
||||||
def apply(directories: Seq[File]): Array[File] = directories.flatMap(directory => wrapNull(directory.listFiles(JarFilter))).toArray
|
def apply(directories: List[File]): Array[File] = toArray(directories.flatMap(directory => wrapNull(directory.listFiles(JarFilter))))
|
||||||
def wrapNull(a: Array[File]): Array[File] = if(a == null) Array() else a
|
def wrapNull(a: Array[File]): Array[File] = if(a == null) Array() else a
|
||||||
|
|
||||||
object JarFilter extends FileFilter
|
object JarFilter extends FileFilter
|
||||||
|
|
@ -45,12 +46,12 @@ object GetJars
|
||||||
object Check
|
object Check
|
||||||
{
|
{
|
||||||
def failIfMissing(loader: ClassLoader, classes: Iterable[String], label: String) =
|
def failIfMissing(loader: ClassLoader, classes: Iterable[String], label: String) =
|
||||||
checkTarget(loader, classes, (), missing => throw new BootException("Could not retrieve " + label + ": missing " + missing.mkString(", ")))
|
checkTarget(loader, classes, (), missing => error("Could not retrieve " + label + ": missing " + missing.mkString(", ")))
|
||||||
def needsUpdate(loader: ClassLoader, classes: Iterable[String]) = checkTarget(loader, classes, false, x => true)
|
def needsUpdate(loader: ClassLoader, classes: Iterable[String]) = checkTarget(loader, classes, false, x => true)
|
||||||
def checkTarget[T](loader: ClassLoader, classes: Iterable[String], ifSuccess: => T, ifFailure: Iterable[String] => T): T =
|
def checkTarget[T](loader: ClassLoader, classes: Iterable[String], ifSuccess: => T, ifFailure: Iterable[String] => T): T =
|
||||||
{
|
{
|
||||||
def classMissing(c: String) = try { Class.forName(c, false, loader); false } catch { case e: ClassNotFoundException => true }
|
def classMissing(c: String) = try { Class.forName(c, false, loader); false } catch { case e: ClassNotFoundException => true }
|
||||||
val missing = classes.filter(classMissing)
|
val missing = classes.toList.filter(classMissing)
|
||||||
if(missing.isEmpty) ifSuccess else ifFailure(missing)
|
if(missing.isEmpty) ifSuccess else ifFailure(missing)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,15 +1,14 @@
|
||||||
package xsbt.boot
|
package xsbt.boot
|
||||||
|
|
||||||
|
import Pre._
|
||||||
import java.io.{File, FileInputStream}
|
import java.io.{File, FileInputStream}
|
||||||
import java.util.Properties
|
import java.util.Properties
|
||||||
|
|
||||||
final case class ResolvedVersion(v: String, wasExplicit: Boolean) extends NotNull
|
|
||||||
|
|
||||||
object ResolveVersions
|
object ResolveVersions
|
||||||
{
|
{
|
||||||
def apply(conf: LaunchConfiguration): LaunchConfiguration = (new ResolveVersions(conf))()
|
def apply(conf: LaunchConfiguration): LaunchConfiguration = (new ResolveVersions(conf))()
|
||||||
private def trim(s: String) = if(s eq null) None else notEmpty(s.trim)
|
private def trim(s: String) = if(s eq null) None else notEmpty(s.trim)
|
||||||
private def notEmpty(s: String) = if(s.isEmpty) None else Some(s)
|
private def notEmpty(s: String) = if(isEmpty(s)) None else Some(s)
|
||||||
private def readProperties(propertiesFile: File) =
|
private def readProperties(propertiesFile: File) =
|
||||||
{
|
{
|
||||||
val properties = new Properties
|
val properties = new Properties
|
||||||
|
|
@ -40,7 +39,7 @@ final class ResolveVersions(conf: LaunchConfiguration) extends NotNull
|
||||||
v match
|
v match
|
||||||
{
|
{
|
||||||
case e: Version.Explicit => e.value
|
case e: Version.Explicit => e.value
|
||||||
case Version.Implicit(default) => readVersion() orElse default getOrElse noVersionInFile
|
case i: Version.Implicit => readVersion() orElse i.default getOrElse noVersionInFile
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
def readVersion() = trim(properties.getProperty(versionProperty))
|
def readVersion() = trim(properties.getProperty(versionProperty))
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,9 @@
|
||||||
*/
|
*/
|
||||||
package xsbt.boot
|
package xsbt.boot
|
||||||
|
|
||||||
|
import Pre._
|
||||||
import java.io.{File, FileWriter, PrintWriter, Writer}
|
import java.io.{File, FileWriter, PrintWriter, Writer}
|
||||||
|
import java.util.regex.Pattern
|
||||||
|
|
||||||
import org.apache.ivy.{core, plugins, util, Ivy}
|
import org.apache.ivy.{core, plugins, util, Ivy}
|
||||||
import core.LogOptions
|
import core.LogOptions
|
||||||
|
|
@ -23,9 +25,9 @@ import BootConfiguration._
|
||||||
|
|
||||||
sealed trait UpdateTarget extends NotNull { def tpe: String }
|
sealed trait UpdateTarget extends NotNull { def tpe: String }
|
||||||
final object UpdateScala extends UpdateTarget { def tpe = "scala" }
|
final object UpdateScala extends UpdateTarget { def tpe = "scala" }
|
||||||
final case class UpdateApp(id: Application) extends UpdateTarget { def tpe = "app" }
|
final class UpdateApp(val id: Application) extends UpdateTarget { def tpe = "app" }
|
||||||
|
|
||||||
final class UpdateConfiguration(val bootDirectory: File, val scalaVersion: String, val repositories: Seq[Repository]) extends NotNull
|
final class UpdateConfiguration(val bootDirectory: File, val scalaVersion: String, val repositories: List[Repository]) extends NotNull
|
||||||
|
|
||||||
/** Ensures that the Scala and application jars exist for the given versions or else downloads them.*/
|
/** Ensures that the Scala and application jars exist for the given versions or else downloads them.*/
|
||||||
final class Update(config: UpdateConfiguration)
|
final class Update(config: UpdateConfiguration)
|
||||||
|
|
@ -58,7 +60,7 @@ final class Update(config: UpdateConfiguration)
|
||||||
case e: Exception =>
|
case e: Exception =>
|
||||||
e.printStackTrace(logWriter)
|
e.printStackTrace(logWriter)
|
||||||
log(e.toString)
|
log(e.toString)
|
||||||
println(" (see " + logFile + " for complete log)")
|
System.out.println(" (see " + logFile + " for complete log)")
|
||||||
}
|
}
|
||||||
finally
|
finally
|
||||||
{
|
{
|
||||||
|
|
@ -80,7 +82,8 @@ final class Update(config: UpdateConfiguration)
|
||||||
case UpdateScala =>
|
case UpdateScala =>
|
||||||
addDependency(moduleID, ScalaOrg, CompilerModuleName, scalaVersion, "default")
|
addDependency(moduleID, ScalaOrg, CompilerModuleName, scalaVersion, "default")
|
||||||
addDependency(moduleID, ScalaOrg, LibraryModuleName, scalaVersion, "default")
|
addDependency(moduleID, ScalaOrg, LibraryModuleName, scalaVersion, "default")
|
||||||
case UpdateApp(app) =>
|
case u: UpdateApp =>
|
||||||
|
val app = u.id
|
||||||
val resolvedName = if(app.crossVersioned) app.name + "_" + scalaVersion else app.name
|
val resolvedName = if(app.crossVersioned) app.name + "_" + scalaVersion else app.name
|
||||||
addDependency(moduleID, app.groupID, resolvedName, app.getVersion, "default(compile)")
|
addDependency(moduleID, app.groupID, resolvedName, app.getVersion, "default(compile)")
|
||||||
}
|
}
|
||||||
|
|
@ -112,8 +115,10 @@ final class Update(config: UpdateConfiguration)
|
||||||
if(resolveReport.hasError)
|
if(resolveReport.hasError)
|
||||||
{
|
{
|
||||||
logExceptions(resolveReport)
|
logExceptions(resolveReport)
|
||||||
println(Set(resolveReport.getAllProblemMessages.toArray: _*).mkString(System.getProperty("line.separator")))
|
val seen = new java.util.LinkedHashSet[Any]
|
||||||
throw new BootException("Error retrieving required libraries")
|
seen.addAll(resolveReport.getAllProblemMessages)
|
||||||
|
System.out.println(seen.toArray.mkString(System.getProperty("line.separator")))
|
||||||
|
error("Error retrieving required libraries")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** Exceptions are logged to the update log file. */
|
/** Exceptions are logged to the update log file. */
|
||||||
|
|
@ -135,7 +140,7 @@ final class Update(config: UpdateConfiguration)
|
||||||
target match
|
target match
|
||||||
{
|
{
|
||||||
case UpdateScala => scalaRetrievePattern
|
case UpdateScala => scalaRetrievePattern
|
||||||
case UpdateApp(app) => appRetrievePattern(app.toID)
|
case u: UpdateApp => appRetrievePattern(u.id.toID)
|
||||||
}
|
}
|
||||||
retrieveEngine.retrieve(module.getModuleRevisionId, baseDirectoryName(scalaVersion) + "/" + pattern, retrieveOptions)
|
retrieveEngine.retrieve(module.getModuleRevisionId, baseDirectoryName(scalaVersion) + "/" + pattern, retrieveOptions)
|
||||||
}
|
}
|
||||||
|
|
@ -144,7 +149,7 @@ final class Update(config: UpdateConfiguration)
|
||||||
{
|
{
|
||||||
val newDefault = new ChainResolver
|
val newDefault = new ChainResolver
|
||||||
newDefault.setName("redefined-public")
|
newDefault.setName("redefined-public")
|
||||||
if(repositories.isEmpty) throw new BootException("No repositories defined.")
|
if(repositories.isEmpty) error("No repositories defined.")
|
||||||
repositories.foreach(repo => newDefault.add(toIvyRepository(settings, repo)))
|
repositories.foreach(repo => newDefault.add(toIvyRepository(settings, repo)))
|
||||||
onDefaultRepositoryCacheManager(settings)(_.setUseOrigin(true))
|
onDefaultRepositoryCacheManager(settings)(_.setUseOrigin(true))
|
||||||
settings.addResolver(newDefault)
|
settings.addResolver(newDefault)
|
||||||
|
|
@ -210,13 +215,13 @@ final class Update(config: UpdateConfiguration)
|
||||||
resolver.addArtifactPattern(localIvyRoot + "/" + LocalArtifactPattern)
|
resolver.addArtifactPattern(localIvyRoot + "/" + LocalArtifactPattern)
|
||||||
resolver
|
resolver
|
||||||
}
|
}
|
||||||
private val SnapshotPattern = """(\d+).(\d+).(\d+)-(\d{8})\.(\d{6})-(\d+|\+)""".r.pattern
|
private val SnapshotPattern = Pattern.compile("""(\d+).(\d+).(\d+)-(\d{8})\.(\d{6})-(\d+|\+)""")
|
||||||
private def scalaSnapshots(scalaVersion: String) =
|
private def scalaSnapshots(scalaVersion: String) =
|
||||||
{
|
{
|
||||||
val m = SnapshotPattern.matcher(scalaVersion)
|
val m = SnapshotPattern.matcher(scalaVersion)
|
||||||
if(m.matches)
|
if(m.matches)
|
||||||
{
|
{
|
||||||
val base = Seq(1,2,3).map(m.group).mkString(".")
|
val base = List(1,2,3).map(m.group).mkString(".")
|
||||||
val pattern = "http://scala-tools.org/repo-snapshots/[organization]/[module]/" + base + "-SNAPSHOT/[artifact]-[revision].[ext]"
|
val pattern = "http://scala-tools.org/repo-snapshots/[organization]/[module]/" + base + "-SNAPSHOT/[artifact]-[revision].[ext]"
|
||||||
|
|
||||||
val resolver = new URLResolver
|
val resolver = new URLResolver
|
||||||
|
|
@ -233,7 +238,7 @@ final class Update(config: UpdateConfiguration)
|
||||||
{
|
{
|
||||||
try { logWriter.println(msg) }
|
try { logWriter.println(msg) }
|
||||||
catch { case e: Exception => System.err.println("Error writing to update log file: " + e.toString) }
|
catch { case e: Exception => System.err.println("Error writing to update log file: " + e.toString) }
|
||||||
println(msg)
|
System.out.println(msg)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
/** A custom logger for Ivy to ignore the messages about not finding classes
|
/** A custom logger for Ivy to ignore the messages about not finding classes
|
||||||
|
|
|
||||||
|
|
@ -10,7 +10,7 @@ object Using extends NotNull
|
||||||
|
|
||||||
object Copy
|
object Copy
|
||||||
{
|
{
|
||||||
def apply(files: Seq[File], toDirectory: File): Unit = files.foreach(file => apply(file, toDirectory))
|
def apply(files: List[File], toDirectory: File): Unit = files.foreach(file => apply(file, toDirectory))
|
||||||
def apply(file: File, toDirectory: File)
|
def apply(file: File, toDirectory: File)
|
||||||
{
|
{
|
||||||
toDirectory.mkdirs()
|
toDirectory.mkdirs()
|
||||||
|
|
|
||||||
|
|
@ -20,19 +20,19 @@ object ScalaProviderTest extends Specification
|
||||||
|
|
||||||
"Launch" should {
|
"Launch" should {
|
||||||
"Successfully load an application from local repository and run it with correct arguments" in {
|
"Successfully load an application from local repository and run it with correct arguments" in {
|
||||||
checkLoad(Array("test"), "xsbt.boot.test.ArgumentTest").asInstanceOf[Exit].code must be(0)
|
checkLoad(List("test"), "xsbt.boot.test.ArgumentTest").asInstanceOf[Exit].code must be(0)
|
||||||
checkLoad(Array(), "xsbt.boot.test.ArgumentTest") must throwA[RuntimeException]
|
checkLoad(List(), "xsbt.boot.test.ArgumentTest") must throwA[RuntimeException]
|
||||||
}
|
}
|
||||||
"Successfully load an application from local repository and run it with correct sbt version" in {
|
"Successfully load an application from local repository and run it with correct sbt version" in {
|
||||||
checkLoad(Array(), "xsbt.boot.test.AppVersionTest").asInstanceOf[Exit].code must be(0)
|
checkLoad(List(), "xsbt.boot.test.AppVersionTest").asInstanceOf[Exit].code must be(0)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private def checkLoad(arguments: Array[String], mainClassName: String): MainResult =
|
private def checkLoad(arguments: List[String], mainClassName: String): MainResult =
|
||||||
FileUtilities.withTemporaryDirectory { currentDirectory =>
|
FileUtilities.withTemporaryDirectory { currentDirectory =>
|
||||||
withLauncher { launcher =>
|
withLauncher { launcher =>
|
||||||
Launch.run(launcher)(
|
Launch.run(launcher)(
|
||||||
RunConfiguration(mapScalaVersion(LaunchTest.getScalaVersion), LaunchTest.testApp(mainClassName).toID, currentDirectory, arguments)
|
new RunConfiguration(mapScalaVersion(LaunchTest.getScalaVersion), LaunchTest.testApp(mainClassName).toID, currentDirectory, arguments)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -49,9 +49,9 @@ object ScalaProviderTest extends Specification
|
||||||
}
|
}
|
||||||
object LaunchTest
|
object LaunchTest
|
||||||
{
|
{
|
||||||
def testApp(main: String) = Application("org.scala-tools.sbt", "launch-test", Version.Explicit(test.MainTest.Version), main, Nil, false)
|
def testApp(main: String) = Application("org.scala-tools.sbt", "launch-test", new Version.Explicit(test.MainTest.Version), main, Nil, false)
|
||||||
import Repository.Predefined._
|
import Repository.Predefined._
|
||||||
def testRepositories = Seq(Local, ScalaToolsReleases, ScalaToolsSnapshots).map(Repository.Predefined.apply)
|
def testRepositories = List(Local, ScalaToolsReleases, ScalaToolsSnapshots).map(Repository.Predefined.apply)
|
||||||
def withLauncher[T](f: xsbti.Launcher => T): T =
|
def withLauncher[T](f: xsbti.Launcher => T): T =
|
||||||
FileUtilities.withTemporaryDirectory { bootDirectory =>
|
FileUtilities.withTemporaryDirectory { bootDirectory =>
|
||||||
f(new Launch(bootDirectory, testRepositories))
|
f(new Launch(bootDirectory, testRepositories))
|
||||||
|
|
@ -59,7 +59,7 @@ object LaunchTest
|
||||||
|
|
||||||
def mapScalaVersion(versionNumber: String) = scalaVersionMap.find(_._2 == versionNumber).getOrElse {
|
def mapScalaVersion(versionNumber: String) = scalaVersionMap.find(_._2 == versionNumber).getOrElse {
|
||||||
error("Scala version number " + versionNumber + " from library.properties has no mapping")}._1
|
error("Scala version number " + versionNumber + " from library.properties has no mapping")}._1
|
||||||
val scalaVersionMap = Map("2.7.2" -> "2.7.2") ++ Seq("2.7.3", "2.7.4", "2.7.5").map(v => (v, v + ".final"))
|
val scalaVersionMap = Map( ("2.7.2", "2.7.2") ) ++ List("2.7.3", "2.7.4", "2.7.5").map(v => (v, v + ".final"))
|
||||||
def getScalaVersion: String = getScalaVersion(getClass.getClassLoader)
|
def getScalaVersion: String = getScalaVersion(getClass.getClassLoader)
|
||||||
def getScalaVersion(loader: ClassLoader): String =
|
def getScalaVersion(loader: ClassLoader): String =
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -1,12 +0,0 @@
|
||||||
package xsbt
|
|
||||||
|
|
||||||
class Main extends xsbti.AppMain
|
|
||||||
{
|
|
||||||
def run(configuration: xsbti.AppConfiguration): xsbti.MainResult =
|
|
||||||
{
|
|
||||||
println(configuration.arguments.mkString("\n"))
|
|
||||||
Exit(0)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
final case class Exit(code: Int) extends xsbti.Exit
|
|
||||||
|
|
@ -1,5 +1,5 @@
|
||||||
#Project properties
|
#Project properties
|
||||||
#Sun Sep 13 00:00:33 EDT 2009
|
#Wed Oct 07 13:14:09 EDT 2009
|
||||||
project.organization=org.scala-tools.sbt
|
project.organization=org.scala-tools.sbt
|
||||||
project.name=xsbt
|
project.name=xsbt
|
||||||
sbt.version=0.5.5
|
sbt.version=0.5.5
|
||||||
|
|
|
||||||
|
|
@ -3,7 +3,6 @@ import java.io.File
|
||||||
|
|
||||||
trait ProguardLaunch extends ProguardProject
|
trait ProguardLaunch extends ProguardProject
|
||||||
{
|
{
|
||||||
override def basicOptions = super.basicOptions ++ Seq(keepJLine)
|
|
||||||
def outputJar = rootProject.outputPath / ("xsbt-launch-" + version + ".jar")
|
def outputJar = rootProject.outputPath / ("xsbt-launch-" + version + ".jar")
|
||||||
override def keepClasses =
|
override def keepClasses =
|
||||||
"org.apache.ivy.plugins.resolver.URLResolver" ::
|
"org.apache.ivy.plugins.resolver.URLResolver" ::
|
||||||
|
|
@ -21,11 +20,25 @@ trait ProguardLaunch extends ProguardProject
|
||||||
|
|
||||||
log.debug("proguard configuration ivy jar location: " + ivyJars.mkString(", "))
|
log.debug("proguard configuration ivy jar location: " + ivyJars.mkString(", "))
|
||||||
|
|
||||||
((withJar(ivyJars.toSeq, "Ivy") + "(!META-INF/**,!fr/**,!**/antlib.xml,!**/*.png)") ::
|
val excludeIvyResourcesString = excludeString(excludeIvyResources)
|
||||||
|
((withJar(ivyJars.toSeq, "Ivy") + excludeIvyResourcesString) ::
|
||||||
(withJar(jlineJars, "JLine") + "(!META-INF/**)" ) ::
|
(withJar(jlineJars, "JLine") + "(!META-INF/**)" ) ::
|
||||||
otherJars.map(jar => mkpath(jar) + "(!META-INF/**,!*.properties)").toList) map { "-injars " + _ }
|
otherJars.map(jar => mkpath(jar) + "(!META-INF/**,!*.properties)").toList) map { "-injars " + _ }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def excludeString(s: List[String]) = s.map("!" + _).mkString("(",",",")")
|
||||||
|
private def excludeIvyResources =
|
||||||
|
"META-INF/**" ::
|
||||||
|
"fr/**" ::
|
||||||
|
"**/antlib.xml" ::
|
||||||
|
"**/*.png" ::
|
||||||
|
"org/apache/ivy/core/settings/ivyconf*.xml" ::
|
||||||
|
"org/apache/ivy/core/settings/ivysettings-*.xml" ::
|
||||||
|
"org/apache/ivy/plugins/resolver/packager/*" ::
|
||||||
|
"**/ivy_vfs.xml" ::
|
||||||
|
"org/apache/ivy/plugins/report/ivy-report-*" ::
|
||||||
|
Nil
|
||||||
|
|
||||||
private def withJar[T](files: Seq[File], name: String) = mkpath(files.firstOption.getOrElse(error(name + " not present (try running update)")))
|
private def withJar[T](files: Seq[File], name: String) = mkpath(files.firstOption.getOrElse(error(name + " not present (try running update)")))
|
||||||
private def isJLineJar(file: File) = isJarX(file, "jline")
|
private def isJLineJar(file: File) = isJarX(file, "jline")
|
||||||
private def isJarX(file: File, x: String) =
|
private def isJarX(file: File, x: String) =
|
||||||
|
|
|
||||||
|
|
@ -36,6 +36,7 @@ trait ProguardProject extends BasicScalaProject
|
||||||
|
|
||||||
def mapInJars(inJars: Seq[File]): Seq[String] = inJars.map(f => "-injars " + mkpath(f))
|
def mapInJars(inJars: Seq[File]): Seq[String] = inJars.map(f => "-injars " + mkpath(f))
|
||||||
def mapLibraryJars(libraryJars: Seq[File]): Seq[String] = libraryJars.map(f => "-libraryjars " + mkpath(f))
|
def mapLibraryJars(libraryJars: Seq[File]): Seq[String] = libraryJars.map(f => "-libraryjars " + mkpath(f))
|
||||||
|
def mapOutJar(outJar: File) = "-outjars " + mkpath(outJar)
|
||||||
|
|
||||||
def template(inJars: Seq[File], libraryJars: Seq[File], outJar: File, options: Seq[String], mainClass: Option[String], keepClasses: Seq[String]) =
|
def template(inJars: Seq[File], libraryJars: Seq[File], outJar: File, options: Seq[String], mainClass: Option[String], keepClasses: Seq[String]) =
|
||||||
{
|
{
|
||||||
|
|
@ -49,7 +50,7 @@ trait ProguardProject extends BasicScalaProject
|
||||||
keepClasses.map("-keep public class " + _ + " {\n public * ;\n}") ++
|
keepClasses.map("-keep public class " + _ + " {\n public * ;\n}") ++
|
||||||
mapInJars(inJars) ++
|
mapInJars(inJars) ++
|
||||||
Seq("-injars " + mkpath(rawJarPath.asFile),
|
Seq("-injars " + mkpath(rawJarPath.asFile),
|
||||||
"-outjars " + mkpath(outJar)) ++
|
mapOutJar(outJar)) ++
|
||||||
mapLibraryJars(libraryJars) ++
|
mapLibraryJars(libraryJars) ++
|
||||||
mainClass.map(main => keepMain.stripMargin.format(main)).toList
|
mainClass.map(main => keepMain.stripMargin.format(main)).toList
|
||||||
lines.mkString("\n")
|
lines.mkString("\n")
|
||||||
|
|
|
||||||
|
|
@ -26,7 +26,6 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
|
||||||
launchInterfaceSub, interfaceSub, ivySub, ioSub, classpathSub, compileInterfaceSub)
|
launchInterfaceSub, interfaceSub, ivySub, ioSub, classpathSub, compileInterfaceSub)
|
||||||
val stdTaskSub = project(tasksPath / "standard", "Standard Tasks", new StandardTaskProject(_), trackingSub, compilerSub)
|
val stdTaskSub = project(tasksPath / "standard", "Standard Tasks", new StandardTaskProject(_), trackingSub, compilerSub)
|
||||||
|
|
||||||
val mainSub = project("main", "Main", new Base(_), stdTaskSub)
|
|
||||||
val distSub = project("dist", "Distribution", new DistProject(_))
|
val distSub = project("dist", "Distribution", new DistProject(_))
|
||||||
|
|
||||||
/* Multi-subproject paths */
|
/* Multi-subproject paths */
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue