back end side of auto Scala version detection in launcher

This commit is contained in:
Mark Harrah 2012-02-04 21:10:30 -05:00
parent 1fd1b7803c
commit 8b06887753
3 changed files with 47 additions and 24 deletions

View File

@ -96,7 +96,7 @@ class Launch private[xsbt](val bootDirectory: File, val lockBoot: Boolean, val i
class JNAProvider extends Provider
{
lazy val id = new Application("net.java.dev.jna", "jna", new Explicit("3.2.3"), "", Nil, false, array())
lazy val configuration = new UpdateConfiguration(bootDirectory, ivyOptions.ivyHome, "", repositories, checksumsList)
lazy val configuration = new UpdateConfiguration(bootDirectory, ivyOptions.ivyHome, None, repositories, checksumsList)
lazy val libDirectory = new File(bootDirectory, baseDirectoryName(""))
def baseDirectories: List[File] = new File(libDirectory, appDirectoryName(id.toID, File.separator)) :: Nil
def testLoadClasses: List[String] = "com.sun.jna.Function" :: Nil
@ -112,7 +112,7 @@ class Launch private[xsbt](val bootDirectory: File, val lockBoot: Boolean, val i
def launcher: xsbti.Launcher = Launch.this
def parentLoader = topLoader
lazy val configuration = new UpdateConfiguration(bootDirectory, ivyOptions.ivyHome, version, repositories, checksumsList)
lazy val configuration = new UpdateConfiguration(bootDirectory, ivyOptions.ivyHome, Some(version), repositories, checksumsList)
lazy val libDirectory = new File(configuration.bootDirectory, baseDirectoryName(version))
lazy val scalaHome = new File(libDirectory, ScalaDirectoryName)
def compilerJar = new File(scalaHome, CompilerModuleName + ".jar")

View File

@ -27,7 +27,7 @@ trait Provider
def retrieveCorrupt(missing: Iterable[String]): Nothing = fail(": missing " + missing.mkString(", "))
private def fail(extra: String) =
throw new xsbti.RetrieveException(versionString, "Could not retrieve " + failLabel + extra)
private def versionString: String = target match { case _: UpdateScala => configuration.scalaVersion; case a: UpdateApp => Value.get(a.id.version) }
private def versionString: String = target match { case _: UpdateScala => configuration.getScalaVersion; case a: UpdateApp => Value.get(a.id.version) }
val (jars, loader) = Locks(orNull(lockFile), new initialize)
private[this] def orNull[T >: Null](opt: Option[T]): T = opt match { case None => null; case Some(x) => x }
@ -40,8 +40,8 @@ trait Provider
(existingJars, existingLoader)
else
{
val retrieveSuccess = ( new Update(configuration) )(target, reason)
if(retrieveSuccess)
val result = ( new Update(configuration) )(target, reason)
if(result.success)
{
val (newJars, newLoader) = createLoader
val missing = Provider.getMissing(newLoader, testLoadClasses)

View File

@ -33,12 +33,16 @@ sealed trait UpdateTarget { def tpe: String; def classifiers: List[String] }
final class UpdateScala(val classifiers: List[String]) extends UpdateTarget { def tpe = "scala" }
final class UpdateApp(val id: Application, val classifiers: List[String]) extends UpdateTarget { def tpe = "app" }
final class UpdateConfiguration(val bootDirectory: File, val ivyHome: Option[File], val scalaVersion: String, val repositories: List[xsbti.Repository], val checksums: List[String])
final class UpdateConfiguration(val bootDirectory: File, val ivyHome: Option[File], val scalaVersion: Option[String], val repositories: List[xsbti.Repository], val checksums: List[String]) {
def getScalaVersion = scalaVersion match { case Some(sv) => sv; case None => "" }
}
final class UpdateResult(val success: Boolean, val scalaVersion: Option[String])
/** Ensures that the Scala and application jars exist for the given versions or else downloads them.*/
final class Update(config: UpdateConfiguration)
{
import config.{bootDirectory, checksums, ivyHome, repositories, scalaVersion}
import config.{bootDirectory, checksums, getScalaVersion, ivyHome, repositories, scalaVersion}
bootDirectory.mkdirs
private def logFile = new File(bootDirectory, UpdateLogName)
@ -58,7 +62,7 @@ final class Update(config: UpdateConfiguration)
val List(realm, host, user, password) = keys.productIterator.map(key => props.getProperty(key.toString)).toList
if (realm != null && host != null && user != null && password != null)
CredentialsStore.INSTANCE.addCredentials(realm, host, user, password)
}
}
private lazy val settings =
{
addCredentials()
@ -68,9 +72,11 @@ final class Update(config: UpdateConfiguration)
settings.setVariable("ivy.checksums", checksums mkString ",")
settings.setDefaultConflictManager(settings.getConflictManager(ConflictManagerName))
settings.setBaseDir(bootDirectory)
settings.setVariable("scala", scalaVersion)
setScalaVariable(settings, scalaVersion)
settings
}
private[this] def setScalaVariable(settings: IvySettings, scalaVersion: Option[String]): Unit =
scalaVersion foreach { sv => settings.setVariable("scala", sv) }
private lazy val ivy =
{
val ivy = new Ivy() { private val loggerEngine = new SbtMessageLoggerEngine; override def getLoggerEngine = loggerEngine }
@ -82,23 +88,23 @@ final class Update(config: UpdateConfiguration)
private lazy val ivyLockFile = new File(settings.getDefaultIvyUserDir, ".sbt.ivy.lock")
/** The main entry point of this class for use by the Update module. It runs Ivy */
def apply(target: UpdateTarget, reason: String): Boolean =
def apply(target: UpdateTarget, reason: String): UpdateResult =
{
Message.setDefaultLogger(new SbtIvyLogger(logWriter))
val action = new Callable[Boolean] { def call = lockedApply(target, reason) }
val action = new Callable[UpdateResult] { def call = lockedApply(target, reason) }
Locks(ivyLockFile, action)
}
private def lockedApply(target: UpdateTarget, reason: String) =
private def lockedApply(target: UpdateTarget, reason: String): UpdateResult =
{
ivy.pushContext()
try { update(target, reason); true }
try { update(target, reason) }
catch
{
case e: Exception =>
e.printStackTrace(logWriter)
log(e.toString)
System.out.println(" (see " + logFile + " for complete log)")
false
new UpdateResult(false, None)
}
finally
{
@ -107,7 +113,7 @@ final class Update(config: UpdateConfiguration)
}
}
/** Runs update for the specified target (updates either the scala or appliciation jars for building the project) */
private def update(target: UpdateTarget, reason: String)
private def update(target: UpdateTarget, reason: String): UpdateResult =
{
import IvyConfiguration.Visibility.PUBLIC
// the actual module id here is not that important
@ -118,13 +124,17 @@ final class Update(config: UpdateConfiguration)
target match
{
case u: UpdateScala =>
val scalaVersion = getScalaVersion
addDependency(moduleID, ScalaOrg, CompilerModuleName, scalaVersion, "default;optional(default)", u.classifiers)
addDependency(moduleID, ScalaOrg, LibraryModuleName, scalaVersion, "default", u.classifiers)
excludeJUnit(moduleID)
System.out.println("Getting Scala " + scalaVersion + " " + reason + "...")
case u: UpdateApp =>
val app = u.id
val resolvedName = if(app.crossVersioned) app.name + "_" + scalaVersion else app.name
val resolvedName = (app.crossVersioned, scalaVersion) match {
case (true, Some(sv)) => app.name + "_" + sv
case _ => app.name
}
addDependency(moduleID, app.groupID, resolvedName, app.getVersion, "default(compile)", u.classifiers)
excludeScala(moduleID)
System.out.println("Getting " + app.groupID + " " + resolvedName + " " + app.getVersion + " " + reason + "...")
@ -132,11 +142,13 @@ final class Update(config: UpdateConfiguration)
update(moduleID, target)
}
/** Runs the resolve and retrieve for the given moduleID, which has had its dependencies added already. */
private def update(moduleID: DefaultModuleDescriptor, target: UpdateTarget)
private def update(moduleID: DefaultModuleDescriptor, target: UpdateTarget): UpdateResult =
{
val eventManager = new EventManager
resolve(eventManager, moduleID)
retrieve(eventManager, moduleID, target)
val autoScalaVersion = resolve(eventManager, moduleID)
setScalaVariable(settings, autoScalaVersion)
retrieve(eventManager, moduleID, target, autoScalaVersion)
new UpdateResult(true, autoScalaVersion)
}
private def createID(organization: String, name: String, revision: String) =
ModuleRevisionId.newInstance(organization, name, revision)
@ -173,7 +185,8 @@ final class Update(config: UpdateConfiguration)
rule.addConfiguration(DefaultIvyConfiguration)
rule
}
private def resolve(eventManager: EventManager, module: ModuleDescriptor)
// returns the version of any Scala dependency
private def resolve(eventManager: EventManager, module: ModuleDescriptor): Option[String] =
{
val resolveOptions = new ResolveOptions
// this reduces the substantial logging done by Ivy, including the progress dots when downloading artifacts
@ -189,7 +202,16 @@ final class Update(config: UpdateConfiguration)
System.out.println(seen.toArray.mkString(System.getProperty("line.separator")))
error("Error retrieving required libraries")
}
scalaDependencyVersion(resolveReport).headOption
}
private[this] def scalaDependencyVersion(report: ResolveReport): List[String] =
for {
config <- report.getConfigurations.toList
module <- report.getConfigurationReport(config).getModuleRevisionIds.toArray collect { case revId: ModuleRevisionId => revId }
if module.getOrganisation == ScalaOrg && module.getName == LibraryModuleName
} yield
module.getRevision
/** Exceptions are logged to the update log file. */
private def logExceptions(report: ResolveReport)
{
@ -204,7 +226,7 @@ final class Update(config: UpdateConfiguration)
def accept(o: Any) = o match { case a: IArtifact => f(a); case _ => false }
}
/** Retrieves resolved dependencies using the given target to determine the location to retrieve to. */
private def retrieve(eventManager: EventManager, module: ModuleDescriptor, target: UpdateTarget)
private def retrieve(eventManager: EventManager, module: ModuleDescriptor, target: UpdateTarget, autoScalaVersion: Option[String])
{
val retrieveOptions = new RetrieveOptions
retrieveOptions.setArtifactFilter(new ArtifactFilter(a => retrieveType(a.getType) && a.getExtraAttribute("classifier") == null))
@ -215,7 +237,8 @@ final class Update(config: UpdateConfiguration)
case _: UpdateScala => scalaRetrievePattern
case u: UpdateApp => appRetrievePattern(u.id.toID)
}
retrieveEngine.retrieve(module.getModuleRevisionId, baseDirectoryName(scalaVersion) + "/" + pattern, retrieveOptions)
val scalaV = scalaVersion orElse autoScalaVersion getOrElse ""
retrieveEngine.retrieve(module.getModuleRevisionId, baseDirectoryName(scalaV) + "/" + pattern, retrieveOptions)
}
private def retrieveType(tpe: String): Boolean = tpe == "jar" || tpe == "bundle"
/** Add the scala tools repositories and a URL resolver to download sbt from the Google code project.*/
@ -240,7 +263,7 @@ final class Update(config: UpdateConfiguration)
artifact.getQualifiedExtraAttributes.keys.exists(_.asInstanceOf[String] startsWith "m:")
}
// exclude the local Maven repository for Scala -SNAPSHOTs
private def includeRepo(repo: xsbti.Repository) = !(Repository.isMavenLocal(repo) && isSnapshot(scalaVersion) )
private def includeRepo(repo: xsbti.Repository) = !(Repository.isMavenLocal(repo) && isSnapshot(getScalaVersion) )
private def isSnapshot(scalaVersion: String) = scalaVersion.endsWith(Snapshot)
private[this] val Snapshot = "-SNAPSHOT"
private[this] val ChangingPattern = ".*" + Snapshot
@ -266,7 +289,7 @@ final class Update(config: UpdateConfiguration)
case MavenLocal => mavenLocal
case MavenCentral => mavenMainResolver
case ScalaToolsReleases => mavenResolver("Scala-Tools Maven2 Repository", "http://scala-tools.org/repo-releases")
case ScalaToolsSnapshots => scalaSnapshots(scalaVersion)
case ScalaToolsSnapshots => scalaSnapshots(getScalaVersion)
}
}
}