mirror of https://github.com/sbt/sbt.git
General support for searching for the working directory to use for the launched application
This commit is contained in:
parent
859767f0e1
commit
34fd967c66
|
|
@ -35,9 +35,4 @@ class AnalyzingCompiler(scalaInstance: ScalaInstance, manager: ComponentManager)
|
|||
new DualLoader(scalaLoader, notXsbtiFilter, x => true, sbtLoader, xsbtiFilter, x => false)
|
||||
}
|
||||
override def toString = "Analyzing compiler (Scala " + scalaInstance.actualVersion + ")"
|
||||
}/*
|
||||
object AnalyzingCompiler
|
||||
{
|
||||
def apply(scalaVersion: String, provider: xsbti.ScalaProvider, manager: ComponentManager): AnalyzingCompiler =
|
||||
new AnalyzingCompiler(ScalaInstance(scalaVersion, provider), manager)
|
||||
}*/
|
||||
}
|
||||
|
|
@ -10,7 +10,7 @@ object Boot
|
|||
{
|
||||
def main(args: Array[String])
|
||||
{
|
||||
System.setProperty("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()
|
||||
try { Launch(args) }
|
||||
catch
|
||||
|
|
|
|||
|
|
@ -5,7 +5,7 @@ import java.net.{URI, URL}
|
|||
|
||||
object Configuration
|
||||
{
|
||||
def parse(file: URL, baseDirectory: File) = Using( new InputStreamReader(file.openStream, "utf8") )( (new ConfigurationParser(baseDirectory)).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]) =
|
||||
args match
|
||||
{
|
||||
|
|
|
|||
|
|
@ -5,10 +5,10 @@ import scala.util.parsing.input.{Reader, StreamReader}
|
|||
|
||||
import java.lang.Character.isWhitespace
|
||||
import java.io.File
|
||||
import java.net.{MalformedURLException, URL}
|
||||
|
||||
class ConfigurationParser(workingDirectory: File) extends Parsers with NotNull
|
||||
class ConfigurationParser extends Parsers with NotNull
|
||||
{
|
||||
def this() = this(new File("."))
|
||||
import scala.util.parsing.input.CharSequenceReader
|
||||
def apply(s: String): LaunchConfiguration = processResult(configuration(new CharSequenceReader(s, 0)))
|
||||
def apply(source: java.io.Reader): LaunchConfiguration = processResult(configuration(StreamReader(source)))
|
||||
|
|
@ -55,18 +55,27 @@ class ConfigurationParser(workingDirectory: File) extends Parsers with NotNull
|
|||
val result = map(name).map(value => trim(value.split(",")).filter(!_.isEmpty)).getOrElse(default)
|
||||
(result, map - name)
|
||||
}
|
||||
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) =
|
||||
(map.getOrElse(name, None).map(p => new File(p)).getOrElse(default), map - name)
|
||||
(map.getOrElse(name, None).map(toFile).getOrElse(default), map - name)
|
||||
|
||||
def getBoot(m: LabelMap): BootSetup =
|
||||
{
|
||||
val (dir, m1) = file(m, "directory", new File(workingDirectory, "project/boot"))
|
||||
val (props, m2) = file(m1, "properties", new File("project/build.properties"))
|
||||
check(m2, "label")
|
||||
BootSetup(dir, props)
|
||||
val (dir, m1) = file(m, "directory", toFile("project/boot"))
|
||||
val (props, m2) = file(m1, "properties", toFile("project/build.properties"))
|
||||
val (search, m3) = getSearch(m2, props)
|
||||
check(m3, "label")
|
||||
BootSetup(dir, props, search)
|
||||
}
|
||||
def getLogging(m: LabelMap): Logging = check("label", process(m, "level", getLevel))
|
||||
def getLevel(m: Option[String]) = m.map(l => LogLevel(l).fold(error, identity[Logging]) ).getOrElse(Logging(LogLevel.Info))
|
||||
def getLevel(m: Option[String]) = m.map(LogLevel.apply).getOrElse(Logging(LogLevel.Info))
|
||||
def getSearch(m: LabelMap, defaultPath: File): (Search, LabelMap) =
|
||||
ids(m, "search", Nil) match
|
||||
{
|
||||
case (Nil, newM) => (Search.none, newM)
|
||||
case (Seq(tpe), newM) => (Search(tpe, Seq(defaultPath)), newM)
|
||||
case (Seq(tpe, paths @ _ *), newM) => (Search(tpe, paths.map(toFile)), newM)
|
||||
}
|
||||
|
||||
def getApplication(m: LabelMap): Application =
|
||||
{
|
||||
|
|
@ -83,11 +92,10 @@ class ConfigurationParser(workingDirectory: File) extends Parsers with NotNull
|
|||
{
|
||||
import Repository.{Ivy, Maven, Predefined}
|
||||
m.toSeq.map {
|
||||
case (key, None) => Predefined(key).fold(err => error(err), x => x)
|
||||
case (key, None) => Predefined(key)
|
||||
case (key, Some(value)) =>
|
||||
val r = trim(value.split(",",2))
|
||||
val url = r(0)
|
||||
if(url.isEmpty) error("No URL specified for '" + key + "'")
|
||||
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)
|
||||
}
|
||||
}
|
||||
|
|
|
|||
|
|
@ -0,0 +1,48 @@
|
|||
package xsbt.boot
|
||||
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
|
||||
object Find { def apply(config: LaunchConfiguration, currentDirectory: File) = (new Find(config))(currentDirectory) }
|
||||
class Find(config: LaunchConfiguration) extends NotNull
|
||||
{
|
||||
import config.boot.search
|
||||
def apply(currentDirectory: File) =
|
||||
{
|
||||
val current = currentDirectory.getCanonicalFile
|
||||
assert(current.isDirectory)
|
||||
|
||||
lazy val fromRoot = path(current, Nil).filter(hasProject).map(_.getCanonicalFile)
|
||||
val found: Option[File] =
|
||||
search.tpe match
|
||||
{
|
||||
case Search.RootFirst => fromRoot.headOption
|
||||
case Search.Nearest => fromRoot.lastOption
|
||||
case Search.Only =>
|
||||
if(hasProject(current))
|
||||
Some(current)
|
||||
else
|
||||
fromRoot match
|
||||
{
|
||||
case Nil => Some(current)
|
||||
case head :: Nil => Some(head)
|
||||
case xs =>
|
||||
System.err.println("Search method is 'only' and multiple ancestor directories match:\n\t" + fromRoot.mkString("\n\t"))
|
||||
System.exit(1)
|
||||
None
|
||||
}
|
||||
case _ => Some(current)
|
||||
}
|
||||
val baseDirectory = found.getOrElse(current)
|
||||
System.setProperty("user.dir", baseDirectory.getAbsolutePath)
|
||||
(config.map(f => resolve(baseDirectory, f)), baseDirectory)
|
||||
}
|
||||
def resolve(baseDirectory: File, f: File): File =
|
||||
{
|
||||
assert(baseDirectory.isDirectory) // if base directory is not a directory, URI.resolve will not work properly
|
||||
val uri = new URI(null, null, f.getPath, null)
|
||||
new File(baseDirectory.toURI.resolve(uri))
|
||||
}
|
||||
private def hasProject(f: File) = f.isDirectory && search.paths.forall(p => resolve(f, p).exists)
|
||||
private def path(f: File, acc: List[File]): List[File] = if(f eq null) acc else path(f.getParentFile, f :: acc)
|
||||
}
|
||||
|
|
@ -11,7 +11,10 @@ object Launch
|
|||
Configuration.find(arguments, currentDirectory) match { case (configLocation, newArguments) => configured(currentDirectory, configLocation, newArguments) }
|
||||
|
||||
def configured(currentDirectory: File, configLocation: URL, arguments: Seq[String]): Unit =
|
||||
parsed(currentDirectory, Configuration.parse(configLocation, currentDirectory) , arguments)
|
||||
{
|
||||
val config = Configuration.parse(configLocation, currentDirectory)
|
||||
Find(config, currentDirectory) match { case (resolved, baseDirectory) => parsed(baseDirectory, resolved, arguments) }
|
||||
}
|
||||
|
||||
def parsed(currentDirectory: File, parsed: LaunchConfiguration, arguments: Seq[String]): Unit =
|
||||
ResolveVersions(parsed) match { case (resolved, finish) => explicit(currentDirectory, resolved, arguments, finish) }
|
||||
|
|
@ -20,7 +23,7 @@ object Launch
|
|||
launch( run(new Launch(explicit.boot.directory, explicit.repositories)) ) (
|
||||
RunConfiguration(explicit.getScalaVersion, explicit.app.toID, currentDirectory, arguments, setupComplete) )
|
||||
|
||||
final def run(launcher: xsbti.Launcher)(config: RunConfiguration): xsbti.MainResult =
|
||||
def run(launcher: xsbti.Launcher)(config: RunConfiguration): xsbti.MainResult =
|
||||
{
|
||||
import config._
|
||||
val scalaProvider: xsbti.ScalaProvider = launcher.getScala(scalaVersion)
|
||||
|
|
|
|||
|
|
@ -1,6 +1,7 @@
|
|||
package xsbt.boot
|
||||
|
||||
import java.io.File
|
||||
import java.net.URL
|
||||
|
||||
final case class LaunchConfiguration(scalaVersion: Version, app: Application, repositories: Seq[Repository], boot: BootSetup, logging: Logging) extends NotNull
|
||||
{
|
||||
|
|
@ -9,6 +10,7 @@ final case class LaunchConfiguration(scalaVersion: Version, app: Application, re
|
|||
def withApp(app: Application) = LaunchConfiguration(scalaVersion, app, repositories, boot, logging)
|
||||
def withAppVersion(newAppVersion: String) = LaunchConfiguration(scalaVersion, app.withVersion(Version.Explicit(newAppVersion)), repositories, boot, logging)
|
||||
def withVersions(newScalaVersion: String, newAppVersion: String) = LaunchConfiguration(Version.Explicit(newScalaVersion), app.withVersion(Version.Explicit(newAppVersion)), repositories, boot, logging)
|
||||
def map(f: File => File) = LaunchConfiguration(scalaVersion, app, repositories, boot.map(f), logging)
|
||||
}
|
||||
|
||||
sealed trait Version extends NotNull
|
||||
|
|
@ -31,9 +33,10 @@ object Version
|
|||
def default = Implicit(Implicit.ReadOrPrompt, None)
|
||||
}
|
||||
|
||||
trait RichEnum extends Enumeration
|
||||
sealed abstract class RichEnum extends Enumeration
|
||||
{
|
||||
def fromString(s: String): Either[String, Value] = valueOf(s).toRight("Expected one of " + elements.mkString(","))
|
||||
def fromString(s: String): Either[String, Value] = valueOf(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
|
||||
|
|
@ -42,7 +45,7 @@ final case class Application(groupID: String, name: String, version: Version, ma
|
|||
def withVersion(newVersion: Version) = Application(groupID, name, newVersion, main, components, crossVersioned)
|
||||
def toID = AppID(groupID, name, getVersion, main, components.toArray, crossVersioned)
|
||||
}
|
||||
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
|
||||
|
||||
object Application
|
||||
{
|
||||
|
|
@ -56,8 +59,8 @@ object Application
|
|||
sealed trait Repository extends NotNull
|
||||
object Repository
|
||||
{
|
||||
final case class Maven(id: String, url: String) extends Repository
|
||||
final case class Ivy(id: String, url: String, pattern: String) extends Repository
|
||||
final case class Maven(id: String, url: URL) extends Repository
|
||||
final case class Ivy(id: String, url: URL, pattern: String) extends Repository
|
||||
final case class Predefined(id: Predefined.Value) extends Repository
|
||||
|
||||
object Predefined extends RichEnum
|
||||
|
|
@ -66,14 +69,28 @@ object Repository
|
|||
val MavenLocal = Value("maven-local")
|
||||
val MavenCentral = Value("maven-central")
|
||||
val ScalaToolsReleases = Value("scala-tools-releases")
|
||||
val ScalaToolsSnapshots = Value("scala-tools-snapshot")
|
||||
def apply(s: String): Either[String, Predefined] = fromString(s).right.map(t => Predefined(t))
|
||||
val ScalaToolsSnapshots = Value("scala-tools-snapshots")
|
||||
def apply(s: String): Predefined = Predefined(toValue(s))
|
||||
}
|
||||
|
||||
def defaults: Seq[Repository] = Predefined.elements.map(Predefined.apply).toList
|
||||
}
|
||||
|
||||
final case class BootSetup(directory: File, properties: File) extends NotNull
|
||||
final case class Search(tpe: Search.Value, paths: Seq[File]) extends NotNull
|
||||
object Search extends RichEnum
|
||||
{
|
||||
def none = Search(Current, Nil)
|
||||
val Only = Value("only")
|
||||
val RootFirst = Value("root-first")
|
||||
val Nearest = Value("nearest")
|
||||
val Current = Value("none")
|
||||
def apply(s: String, paths: Seq[File]): Search = Search(toValue(s), paths)
|
||||
}
|
||||
|
||||
final case class BootSetup(directory: File, properties: File, search: Search) extends NotNull
|
||||
{
|
||||
def map(f: File => File) = BootSetup(f(directory), f(properties), search)
|
||||
}
|
||||
final case class Logging(level: LogLevel.Value) extends NotNull
|
||||
object LogLevel extends RichEnum
|
||||
{
|
||||
|
|
@ -81,11 +98,11 @@ object LogLevel extends RichEnum
|
|||
val Info = Value("info")
|
||||
val Warn = Value("warn")
|
||||
val Error = Value("error")
|
||||
def apply(s: String): Either[String, Logging] = fromString(s).right.map(t => Logging(t))
|
||||
def apply(s: String): Logging = Logging(toValue(s))
|
||||
}
|
||||
|
||||
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).
|
||||
// This indicates overrides toString because the exception class name is not needed to understand
|
||||
// the error message.
|
||||
class BootException(override val toString: String) extends RuntimeException
|
||||
final class BootException(override val toString: String) extends RuntimeException
|
||||
|
|
@ -42,6 +42,7 @@ final class Update(config: UpdateConfiguration)
|
|||
addResolvers(settings)
|
||||
settings.setDefaultConflictManager(settings.getConflictManager(ConflictManagerName))
|
||||
settings.setBaseDir(bootDirectory)
|
||||
settings.setVariable("scala", scalaVersion)
|
||||
settings
|
||||
}
|
||||
private lazy val ivy = Ivy.newInstance(settings)
|
||||
|
|
@ -81,7 +82,7 @@ final class Update(config: UpdateConfiguration)
|
|||
addDependency(moduleID, ScalaOrg, LibraryModuleName, scalaVersion, "default")
|
||||
case UpdateApp(app) =>
|
||||
val resolvedName = if(app.crossVersioned) app.name + "_" + scalaVersion else app.name
|
||||
addDependency(moduleID, app.groupID, resolvedName, app.getVersion, "runtime(default)")
|
||||
addDependency(moduleID, app.groupID, resolvedName, app.getVersion, "default(compile)")
|
||||
}
|
||||
update(moduleID, target)
|
||||
}
|
||||
|
|
@ -155,13 +156,13 @@ final class Update(config: UpdateConfiguration)
|
|||
import Predefined._
|
||||
repo match
|
||||
{
|
||||
case Maven(id, url) => mavenResolver(id, url)
|
||||
case Ivy(id, url, pattern) => urlResolver(id, url, pattern)
|
||||
case Maven(id, url) => mavenResolver(id, url.toString)
|
||||
case Ivy(id, url, pattern) => urlResolver(id, url.toString, pattern)
|
||||
case Predefined(Local) => localResolver(settings.getDefaultIvyUserDir.getAbsolutePath)
|
||||
case Predefined(MavenLocal) => mavenLocal
|
||||
case Predefined(MavenCentral) => mavenMainResolver
|
||||
case Predefined(ScalaToolsReleases) => mavenResolver("Scala-Tools Maven2 Repository", "http://scala-tools.org/repo-releases")
|
||||
case Predefined(ScalaToolsSnapshots) => mavenResolver("Scala-Tools Maven2 Snapshots Repository", "http://scala-tools.org/repo-snapshots")
|
||||
case Predefined(ScalaToolsSnapshots) => scalaSnapshots(scalaVersion)
|
||||
}
|
||||
}
|
||||
private def onDefaultRepositoryCacheManager(settings: IvySettings)(f: DefaultRepositoryCacheManager => Unit)
|
||||
|
|
@ -209,6 +210,24 @@ final class Update(config: UpdateConfiguration)
|
|||
resolver.addArtifactPattern(localIvyRoot + "/" + LocalArtifactPattern)
|
||||
resolver
|
||||
}
|
||||
private val SnapshotPattern = """(\d+).(\d+).(\d+)-(\d{8})\.(\d{6})-(\d+|\+)""".r.pattern
|
||||
private def scalaSnapshots(scalaVersion: String) =
|
||||
{
|
||||
val m = SnapshotPattern.matcher(scalaVersion)
|
||||
if(m.matches)
|
||||
{
|
||||
val base = Seq(1,2,3).map(m.group).mkString(".")
|
||||
val pattern = "http://scala-tools.org/repo-snapshots/[organization]/[module]/" + base + "-SNAPSHOT/[artifact]-[revision].[ext]"
|
||||
|
||||
val resolver = new URLResolver
|
||||
resolver.setName("Scala Tools Snapshots")
|
||||
resolver.setM2compatible(true)
|
||||
resolver.addArtifactPattern(pattern)
|
||||
resolver
|
||||
}
|
||||
else
|
||||
mavenResolver("Scala-Tools Maven2 Snapshots Repository", "http://scala-tools.org/repo-snapshots")
|
||||
}
|
||||
/** Logs the given message to a file and to the console. */
|
||||
private def log(msg: String) =
|
||||
{
|
||||
|
|
|
|||
Loading…
Reference in New Issue