package xsbt.boot
import Pre._
import java.io.{File, FileInputStream, InputStreamReader}
import java.net.{MalformedURLException, URI, URL}
import java.util.regex.Pattern
import scala.collection.immutable.List
import annotation.tailrec
object Configuration
{
final val SysPropPrefix = "-D"
def parse(file: URL, baseDirectory: File) = Using( new InputStreamReader(file.openStream, "utf8") )( (new ConfigurationParser).apply )
@tailrec def find(args: List[String], baseDirectory: File): (URL, List[String]) =
args match
{
case head :: tail if head.startsWith("@") => (directConfiguration(head.substring(1), baseDirectory), tail)
case head :: tail if head.startsWith(SysPropPrefix) =>
setProperty(head stripPrefix SysPropPrefix)
find(tail, baseDirectory)
case _ =>
val propertyConfigured = System.getProperty("sbt.boot.properties")
val url = if(propertyConfigured == null) configurationOnClasspath else configurationFromFile(propertyConfigured, baseDirectory)
(url , args)
}
def setProperty(head: String)
{
val keyValue = head.split("=",2)
if(keyValue.length != 2)
System.err.println("Warning: invalid system property '" + head + "'")
else
System.setProperty(keyValue(0), keyValue(1))
}
def configurationOnClasspath: URL =
{
val paths = resourcePaths(guessSbtVersion)
paths.iterator.map(getClass.getResource).find(neNull) getOrElse
( multiPartError("Could not finder sbt launch configuration. Searched classpath for:", paths))
}
def directConfiguration(path: String, baseDirectory: File): URL =
{
try { new URL(path) }
catch { case _: MalformedURLException => configurationFromFile(path, baseDirectory) }
}
def configurationFromFile(path: String, baseDirectory: File): URL =
{
def resolve(against: URI): Option[URL] =
{
val resolved = against.resolve(path)
val exists = try { (new File(resolved)).exists } catch { case _: IllegalArgumentException => false }
if(exists) Some(resolved.toURL) else None
}
val against = resolveAgainst(baseDirectory)
val resolving = against.iterator.flatMap(e => resolve(e).toList.iterator)
if(!resolving.hasNext) multiPartError("Could not find configuration file '" + path + "'. Searched:", against)
resolving.next()
}
def multiPartError[T](firstLine: String, lines: List[T]) = error( (firstLine :: lines).mkString("\n\t") )
def UnspecifiedVersionPart = "Unspecified"
def DefaultVersionPart = "Default"
def DefaultBuildProperties = "project/build.properties"
def SbtVersionProperty = "sbt.version"
val ConfigurationName = "sbt.boot.properties"
val JarBasePath = "/sbt/"
def userConfigurationPath = "/" + ConfigurationName
def defaultConfigurationPath = JarBasePath + ConfigurationName
val baseResourcePaths: List[String] = userConfigurationPath :: defaultConfigurationPath :: Nil
def resourcePaths(sbtVersion: Option[String]): List[String] =
versionParts(sbtVersion) flatMap { part =>
baseResourcePaths map { base =>
base + part
}
}
def fallbackParts: List[String] = "" :: Nil
def versionParts(version: Option[String]): List[String] =
version match {
case None => UnspecifiedVersionPart :: fallbackParts
case Some(v) => versionParts(v)
}
def versionParts(version: String): List[String] =
{
val pattern = Pattern.compile("""(\d+)\.(\d+)\.(\d+)(-.*)?""")
val m = pattern.matcher(version)
if(m.matches())
subPartsIndices map {_.map(m.group).filter(neNull).mkString(".") }
else
DefaultVersionPart :: fallbackParts
}
private[this] def subPartsIndices =
(1 :: 2 :: Nil) ::
(1 :: 2 :: 3 :: Nil) ::
(1 :: 2 :: 3 :: 4 :: Nil) ::
(Nil) ::
Nil
def guessSbtVersion: Option[String] =
{
val props = ResolveValues.readProperties(new File(DefaultBuildProperties))
Option(props.getProperty(SbtVersionProperty))
}
def resolveAgainst(baseDirectory: File): List[URI] = (baseDirectory toURI) :: (new File(System.getProperty("user.home")) toURI) ::
toDirectory(classLocation(getClass).toURI) :: Nil
def classLocation(cl: Class[_]): URL =
{
val codeSource = cl.getProtectionDomain.getCodeSource
if(codeSource == null) error("No class location for " + cl)
else codeSource.getLocation
}
def toDirectory(uri: URI): URI =
try
{
val file = new File(uri)
val newFile = if(file.isFile) file.getParentFile else file
newFile.toURI
}
catch { case _: Exception => uri }
private[this] def neNull: AnyRef => Boolean = _ ne null
}