mirror of https://github.com/sbt/sbt.git
Merge pull request #1108 from dansanduleac/dynamic-app-launch
Launcher to retrieve dynamic app versions correctly
This commit is contained in:
commit
e4375b3e58
|
|
@ -200,28 +200,34 @@ class Launch private[xsbt](val bootDirectory: File, val lockBoot: Boolean, val i
|
|||
@tailrec private[this] final def getAppProvider0(id: xsbti.ApplicationID, explicitScalaVersion: Option[String], forceAppUpdate: Boolean): xsbti.AppProvider =
|
||||
{
|
||||
val app = appModule(id, explicitScalaVersion, true, "app")
|
||||
val baseDirs = (base: File) => appBaseDirs(base, id)
|
||||
/** Replace the version of an ApplicationID with the given one, if set. */
|
||||
def resolveId(appVersion: Option[String], id: xsbti.ApplicationID) = appVersion map { v =>
|
||||
import id._
|
||||
AppID(groupID(), name(), v, mainClass(), mainComponents(), crossVersionedValue(), classpathExtra())
|
||||
} getOrElse id
|
||||
val baseDirs = (resolvedVersion: Option[String]) => (base: File) => appBaseDirs(base, resolveId(resolvedVersion, id))
|
||||
def retrieve() = {
|
||||
val sv = update(app, "")
|
||||
val (appv, sv) = update(app, "")
|
||||
val scalaVersion = strictOr(explicitScalaVersion, sv)
|
||||
new RetrievedModule(true, app, sv, baseDirs(scalaHome(ScalaOrg, scalaVersion)))
|
||||
new RetrievedModule(true, app, sv, appv, baseDirs(appv)(scalaHome(ScalaOrg, scalaVersion)))
|
||||
}
|
||||
val retrievedApp =
|
||||
if(forceAppUpdate)
|
||||
retrieve()
|
||||
else
|
||||
existing(app, ScalaOrg, explicitScalaVersion, baseDirs) getOrElse retrieve()
|
||||
existing(app, ScalaOrg, explicitScalaVersion, baseDirs(None)) getOrElse retrieve()
|
||||
|
||||
val scalaVersion = getOrError(strictOr(explicitScalaVersion, retrievedApp.detectedScalaVersion), "No Scala version specified or detected")
|
||||
val scalaProvider = getScala(scalaVersion, "(for " + id.name + ")")
|
||||
val resolvedId = resolveId(retrievedApp.resolvedAppVersion, id)
|
||||
|
||||
val (missing, appProvider) = checkedAppProvider(id, retrievedApp, scalaProvider)
|
||||
val (missing, appProvider) = checkedAppProvider(resolvedId, retrievedApp, scalaProvider)
|
||||
if(missing.isEmpty)
|
||||
appProvider
|
||||
else if(retrievedApp.fresh)
|
||||
app.retrieveCorrupt(missing)
|
||||
else
|
||||
getAppProvider0(id, explicitScalaVersion, true)
|
||||
getAppProvider0(resolvedId, explicitScalaVersion, true)
|
||||
}
|
||||
def scalaHome(scalaOrg: String, scalaVersion: Option[String]): File = new File(bootDirectory, baseDirectoryName(scalaOrg, scalaVersion))
|
||||
def appHome(id: xsbti.ApplicationID, scalaVersion: Option[String]): File = appDirectory(scalaHome(ScalaOrg, scalaVersion), id)
|
||||
|
|
@ -248,7 +254,7 @@ class Launch private[xsbt](val bootDirectory: File, val lockBoot: Boolean, val i
|
|||
try Some(provider(mod))
|
||||
catch { case e: Exception => None }
|
||||
} getOrElse {
|
||||
val scalaVersion = update(scalaM, reason)
|
||||
val (_, scalaVersion) = update(scalaM, reason)
|
||||
provider( new RetrievedModule(true, scalaM, scalaVersion, baseDirs) )
|
||||
}
|
||||
}
|
||||
|
|
@ -343,10 +349,11 @@ class Launch private[xsbt](val bootDirectory: File, val lockBoot: Boolean, val i
|
|||
failLabel = "Scala " + version,
|
||||
extraClasspath = array()
|
||||
)
|
||||
def update(mm: ModuleDefinition, reason: String): Option[String] =
|
||||
/** Returns the resolved appVersion (if this was an App), as well as the scalaVersion. */
|
||||
def update(mm: ModuleDefinition, reason: String): (Option[String], Option[String]) =
|
||||
{
|
||||
val result = ( new Update(mm.configuration) )(mm.target, reason)
|
||||
if(result.success) result.scalaVersion else mm.retrieveFailed
|
||||
if(result.success) result.appVersion -> result.scalaVersion else mm.retrieveFailed
|
||||
}
|
||||
}
|
||||
object Launcher
|
||||
|
|
|
|||
|
|
@ -13,8 +13,12 @@ final class ModuleDefinition(val configuration: UpdateConfiguration, val extraCl
|
|||
private def versionString: String = target match { case _: UpdateScala => configuration.getScalaVersion; case a: UpdateApp => Value.get(a.id.version) }
|
||||
}
|
||||
|
||||
final class RetrievedModule(val fresh: Boolean, val definition: ModuleDefinition, val detectedScalaVersion: Option[String], val baseDirectories: List[File])
|
||||
final class RetrievedModule(val fresh: Boolean, val definition: ModuleDefinition, val detectedScalaVersion: Option[String], val resolvedAppVersion: Option[String], val baseDirectories: List[File])
|
||||
{
|
||||
/** Use this constructor only when the module exists already, or when its version is not dynamic (so its resolved version would be the same) */
|
||||
def this(fresh: Boolean, definition: ModuleDefinition, detectedScalaVersion: Option[String], baseDirectories: List[File]) =
|
||||
this(fresh, definition, detectedScalaVersion, None, baseDirectories)
|
||||
|
||||
lazy val classpath: Array[File] = getJars(baseDirectories)
|
||||
lazy val fullClasspath: Array[File] = concat(classpath, definition.extraClasspath)
|
||||
|
||||
|
|
|
|||
|
|
@ -39,7 +39,10 @@ final class UpdateConfiguration(val bootDirectory: File, val ivyHome: Option[Fil
|
|||
def getScalaVersion = scalaVersion match { case Some(sv) => sv; case None => "" }
|
||||
}
|
||||
|
||||
final class UpdateResult(val success: Boolean, val scalaVersion: Option[String])
|
||||
final class UpdateResult(val success: Boolean, val scalaVersion: Option[String], val appVersion: Option[String]) {
|
||||
@deprecated("0.13.2", "Please use the other constructor providing appVersion.")
|
||||
def this(success: Boolean, scalaVersion: Option[String]) = this(success, scalaVersion, None)
|
||||
}
|
||||
|
||||
/** Ensures that the Scala and application jars exist for the given versions or else downloads them.*/
|
||||
final class Update(config: UpdateConfiguration)
|
||||
|
|
@ -109,7 +112,7 @@ final class Update(config: UpdateConfiguration)
|
|||
e.printStackTrace(logWriter)
|
||||
log(e.toString)
|
||||
System.out.println(" (see " + logFile + " for complete log)")
|
||||
new UpdateResult(false, None)
|
||||
new UpdateResult(false, None, None)
|
||||
}
|
||||
finally
|
||||
{
|
||||
|
|
@ -127,15 +130,16 @@ final class Update(config: UpdateConfiguration)
|
|||
moduleID.setLastModified(System.currentTimeMillis)
|
||||
moduleID.addConfiguration(new IvyConfiguration(DefaultIvyConfiguration, PUBLIC, "", new Array(0), true, null))
|
||||
// add dependencies based on which target needs updating
|
||||
target match
|
||||
val dep = 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)
|
||||
val ddesc = addDependency(moduleID, scalaOrg, LibraryModuleName, scalaVersion, "default", u.classifiers)
|
||||
excludeJUnit(moduleID)
|
||||
val scalaOrgString = if (scalaOrg != ScalaOrg) " " + scalaOrg else ""
|
||||
System.out.println("Getting" + scalaOrgString + " Scala " + scalaVersion + " " + reason + "...")
|
||||
ddesc.getDependencyId
|
||||
case u: UpdateApp =>
|
||||
val app = u.id
|
||||
val resolvedName = (app.crossVersioned, scalaVersion) match {
|
||||
|
|
@ -143,24 +147,31 @@ final class Update(config: UpdateConfiguration)
|
|||
case (xsbti.CrossValue.Binary, Some(sv)) => app.name + "_" + CrossVersionUtil.binaryScalaVersion(sv)
|
||||
case _ => app.name
|
||||
}
|
||||
addDependency(moduleID, app.groupID, resolvedName, app.getVersion, "default(compile)", u.classifiers)
|
||||
val ddesc = addDependency(moduleID, app.groupID, resolvedName, app.getVersion, "default(compile)", u.classifiers)
|
||||
System.out.println("Getting " + app.groupID + " " + resolvedName + " " + app.getVersion + " " + reason + "...")
|
||||
ddesc.getDependencyId
|
||||
}
|
||||
update(moduleID, target)
|
||||
update(moduleID, target, dep)
|
||||
}
|
||||
/** Runs the resolve and retrieve for the given moduleID, which has had its dependencies added already. */
|
||||
private def update(moduleID: DefaultModuleDescriptor, target: UpdateTarget): UpdateResult =
|
||||
private def update(moduleID: DefaultModuleDescriptor, target: UpdateTarget, dep: ModuleId): UpdateResult =
|
||||
{
|
||||
val eventManager = new EventManager
|
||||
val autoScalaVersion = resolve(eventManager, moduleID)
|
||||
val (autoScalaVersion, depVersion) = resolve(eventManager, moduleID, dep)
|
||||
// Fix up target.id with the depVersion that we know for sure is resolved (not dynamic) -- this way, `retrieve`
|
||||
// will put them in the right version directory.
|
||||
val target1 = (depVersion, target) match {
|
||||
case (Some(dv), u: UpdateApp) => import u._; new UpdateApp(id.copy(version = new Explicit(dv)), classifiers, tpe)
|
||||
case _ => target
|
||||
}
|
||||
setScalaVariable(settings, autoScalaVersion)
|
||||
retrieve(eventManager, moduleID, target, autoScalaVersion)
|
||||
new UpdateResult(true, autoScalaVersion)
|
||||
retrieve(eventManager, moduleID, target1, autoScalaVersion)
|
||||
new UpdateResult(true, autoScalaVersion, depVersion)
|
||||
}
|
||||
private def createID(organization: String, name: String, revision: String) =
|
||||
ModuleRevisionId.newInstance(organization, name, revision)
|
||||
/** Adds the given dependency to the default configuration of 'moduleID'. */
|
||||
private def addDependency(moduleID: DefaultModuleDescriptor, organization: String, name: String, revision: String, conf: String, classifiers: List[String])
|
||||
private def addDependency(moduleID: DefaultModuleDescriptor, organization: String, name: String, revision: String, conf: String, classifiers: List[String]) =
|
||||
{
|
||||
val dep = new DefaultDependencyDescriptor(moduleID, createID(organization, name, revision), false, false, true)
|
||||
for(c <- conf.split(";"))
|
||||
|
|
@ -168,6 +179,7 @@ final class Update(config: UpdateConfiguration)
|
|||
for(classifier <- classifiers)
|
||||
addClassifier(dep, name, classifier)
|
||||
moduleID.addDependency(dep)
|
||||
dep
|
||||
}
|
||||
private def addClassifier(dep: DefaultDependencyDescriptor, name: String, classifier: String)
|
||||
{
|
||||
|
|
@ -186,8 +198,9 @@ final class Update(config: UpdateConfiguration)
|
|||
rule.addConfiguration(DefaultIvyConfiguration)
|
||||
rule
|
||||
}
|
||||
// returns the version of any Scala dependency
|
||||
private def resolve(eventManager: EventManager, module: ModuleDescriptor): Option[String] =
|
||||
val scalaLibraryId = ModuleId.newInstance(ScalaOrg, LibraryModuleName)
|
||||
// Returns the version of the scala library, as well as `dep` (a dependency of `module`) after it's been resolved
|
||||
private def resolve(eventManager: EventManager, module: ModuleDescriptor, dep: ModuleId): (Option[String], Option[String]) =
|
||||
{
|
||||
val resolveOptions = new ResolveOptions
|
||||
// this reduces the substantial logging done by Ivy, including the progress dots when downloading artifacts
|
||||
|
|
@ -203,18 +216,18 @@ final class Update(config: UpdateConfiguration)
|
|||
System.out.println(seen.toArray.mkString(System.getProperty("line.separator")))
|
||||
error("Error retrieving required libraries")
|
||||
}
|
||||
scalaDependencyVersion(resolveReport).headOption
|
||||
val modules = moduleRevisionIDs(resolveReport)
|
||||
extractVersion(modules, scalaLibraryId) -> extractVersion(modules, dep)
|
||||
}
|
||||
private[this] def scalaDependencyVersion(report: ResolveReport): List[String] =
|
||||
private[this] def extractVersion(modules: Seq[ModuleRevisionId], dep: ModuleId): Option[String] =
|
||||
{
|
||||
val modules = report.getConfigurations.toList flatMap { config =>
|
||||
report.getConfigurationReport(config).getModuleRevisionIds.toArray
|
||||
}
|
||||
modules flatMap {
|
||||
case module: ModuleRevisionId if module.getOrganisation == ScalaOrg && module.getName == LibraryModuleName =>
|
||||
module.getRevision :: Nil
|
||||
case _ => Nil
|
||||
}
|
||||
modules collectFirst { case m if m.getModuleId.equals(dep) => m.getRevision }
|
||||
}
|
||||
private[this] def moduleRevisionIDs(report: ResolveReport): Seq[ModuleRevisionId] =
|
||||
{
|
||||
import collection.JavaConverters._
|
||||
import org.apache.ivy.core.resolve.IvyNode
|
||||
report.getDependencies.asInstanceOf[java.util.List[IvyNode]].asScala map (_.getResolvedId)
|
||||
}
|
||||
|
||||
/** Exceptions are logged to the update log file. */
|
||||
|
|
@ -244,7 +257,8 @@ final class Update(config: UpdateConfiguration)
|
|||
val filter = (a: IArtifact) => retrieveType(a.getType) && a.getExtraAttribute("classifier") == null && extraFilter(a)
|
||||
retrieveOptions.setArtifactFilter(new ArtifactFilter(filter))
|
||||
val scalaV = strictOr(scalaVersion, autoScalaVersion)
|
||||
retrieveEngine.retrieve(module.getModuleRevisionId, baseDirectoryName(scalaOrg, scalaV) + "/" + pattern, retrieveOptions)
|
||||
retrieveOptions.setDestArtifactPattern(baseDirectoryName(scalaOrg, scalaV) + "/" + pattern)
|
||||
retrieveEngine.retrieve(module.getModuleRevisionId, retrieveOptions)
|
||||
}
|
||||
private[this] def notCoreScala(a: IArtifact) = a.getName match {
|
||||
case LibraryModuleName | CompilerModuleName => false
|
||||
|
|
|
|||
Loading…
Reference in New Issue