sbt/launch/Update.scala

235 lines
9.1 KiB
Scala
Raw Normal View History

2009-08-21 14:12:43 +02:00
/* sbt -- Simple Build Tool
* Copyright 2009 Mark Harrah
*/
package xsbt.boot
import java.io.{File, FileWriter, PrintWriter, Writer}
import org.apache.ivy.{core, plugins, util, Ivy}
import core.LogOptions
import core.cache.DefaultRepositoryCacheManager
import core.event.EventManager
import core.module.id.ModuleRevisionId
2009-09-26 08:18:04 +02:00
import core.module.descriptor.{Configuration => IvyConfiguration, DefaultDependencyDescriptor, DefaultModuleDescriptor, ModuleDescriptor}
2009-08-21 14:12:43 +02:00
import core.report.ResolveReport
import core.resolve.{ResolveEngine, ResolveOptions}
import core.retrieve.{RetrieveEngine, RetrieveOptions}
import core.sort.SortEngine
import core.settings.IvySettings
import plugins.resolver.{ChainResolver, FileSystemResolver, IBiblioResolver, URLResolver}
import util.{DefaultMessageLogger, Message}
import BootConfiguration._
2009-09-26 08:18:04 +02:00
object UpdateTarget extends Enumeration
2009-08-21 14:12:43 +02:00
{
2009-09-26 08:18:04 +02:00
val UpdateScala = Value("scala")
val UpdateApp = Value("app")
2009-08-21 14:12:43 +02:00
}
2009-09-26 08:18:04 +02:00
import UpdateTarget.{UpdateApp, UpdateScala}
2009-08-21 14:12:43 +02:00
2009-09-26 08:18:04 +02:00
/** Ensures that the Scala and application jars exist for the given versions or else downloads them.*/
final class Update(config: LaunchConfiguration)
2009-08-21 14:12:43 +02:00
{
2009-09-26 08:18:04 +02:00
private val bootDirectory = config.boot.directory
bootDirectory.mkdirs
private val scalaVersion = config.getScalaVersion
2009-08-21 14:12:43 +02:00
private def logFile = new File(bootDirectory, UpdateLogName)
2009-09-26 08:18:04 +02:00
private val logWriter = new PrintWriter(new FileWriter(logFile))
private lazy val settings =
2009-08-21 14:12:43 +02:00
{
2009-09-26 08:18:04 +02:00
val settings = new IvySettings
addResolvers(settings)
settings.setDefaultConflictManager(settings.getConflictManager(ConflictManagerName))
settings.setBaseDir(bootDirectory)
settings
2009-08-21 14:12:43 +02:00
}
2009-09-26 08:18:04 +02:00
private lazy val ivy = Ivy.newInstance(settings)
2009-08-21 14:12:43 +02:00
/** The main entry point of this class for use by the Update module. It runs Ivy */
def apply(target: UpdateTarget.Value)
{
Message.setDefaultLogger(new SbtIvyLogger(logWriter))
2009-09-26 08:18:04 +02:00
ivy.pushContext()
2009-08-21 14:12:43 +02:00
try { update(target) }
catch
{
case e: Exception =>
e.printStackTrace(logWriter)
log(e.toString)
println(" (see " + logFile + " for complete log)")
}
2009-09-26 08:18:04 +02:00
finally
{
logWriter.close()
ivy.popContext()
}
2009-08-21 14:12:43 +02:00
}
2009-09-26 08:18:04 +02:00
/** Runs update for the specified target (updates either the scala or appliciation jars for building the project) */
2009-08-21 14:12:43 +02:00
private def update(target: UpdateTarget.Value)
{
2009-09-26 08:18:04 +02:00
import IvyConfiguration.Visibility.PUBLIC
2009-08-21 14:12:43 +02:00
// the actual module id here is not that important
2009-09-26 08:18:04 +02:00
val moduleID = new DefaultModuleDescriptor(createID(SbtOrg, "boot-" + target, "1.0"), "release", null, false)
2009-08-21 14:12:43 +02:00
moduleID.setLastModified(System.currentTimeMillis)
2009-09-26 08:18:04 +02:00
moduleID.addConfiguration(new IvyConfiguration(DefaultIvyConfiguration, PUBLIC, "", Array(), true, null))
2009-08-21 14:12:43 +02:00
// add dependencies based on which target needs updating
target match
{
case UpdateScala =>
2009-09-26 08:18:04 +02:00
addDependency(moduleID, ScalaOrg, CompilerModuleName, scalaVersion, "default")
addDependency(moduleID, ScalaOrg, LibraryModuleName, scalaVersion, "default")
case UpdateApp =>
val resolvedName = if(config.app.crossVersioned) config.app.name + "_" + scalaVersion else config.app.name
addDependency(moduleID, config.app.groupID, resolvedName, config.app.getVersion, "runtime(default)")
2009-08-21 14:12:43 +02:00
}
2009-09-26 08:18:04 +02:00
update(moduleID, target)
2009-08-21 14:12:43 +02:00
}
/** Runs the resolve and retrieve for the given moduleID, which has had its dependencies added already. */
2009-09-26 08:18:04 +02:00
private def update(moduleID: DefaultModuleDescriptor, target: UpdateTarget.Value)
2009-08-21 14:12:43 +02:00
{
val eventManager = new EventManager
2009-09-26 08:18:04 +02:00
resolve(eventManager, moduleID)
retrieve(eventManager, moduleID, target)
2009-08-21 14:12:43 +02:00
}
private def createID(organization: String, name: String, revision: String) =
ModuleRevisionId.newInstance(organization, name, revision)
/** Adds the given dependency to the default configuration of 'moduleID'. */
2009-09-26 08:18:04 +02:00
private def addDependency(moduleID: DefaultModuleDescriptor, organization: String, name: String, revision: String, conf: String)
2009-08-21 14:12:43 +02:00
{
val dep = new DefaultDependencyDescriptor(moduleID, createID(organization, name, revision), false, false, true)
2009-09-26 08:18:04 +02:00
dep.addDependencyConfiguration(DefaultIvyConfiguration, conf)
2009-08-21 14:12:43 +02:00
moduleID.addDependency(dep)
}
2009-09-26 08:18:04 +02:00
private def resolve(eventManager: EventManager, module: ModuleDescriptor)
2009-08-21 14:12:43 +02:00
{
val resolveOptions = new ResolveOptions
// this reduces the substantial logging done by Ivy, including the progress dots when downloading artifacts
resolveOptions.setLog(LogOptions.LOG_DOWNLOAD_ONLY)
val resolveEngine = new ResolveEngine(settings, eventManager, new SortEngine(settings))
val resolveReport = resolveEngine.resolve(module, resolveOptions)
if(resolveReport.hasError)
{
logExceptions(resolveReport)
println(Set(resolveReport.getAllProblemMessages.toArray: _*).mkString(System.getProperty("line.separator")))
throw new BootException("Error retrieving required libraries")
}
}
/** Exceptions are logged to the update log file. */
private def logExceptions(report: ResolveReport)
{
for(unresolved <- report.getUnresolvedDependencies)
{
val problem = unresolved.getProblem
if(problem != null)
problem.printStackTrace(logWriter)
}
}
/** Retrieves resolved dependencies using the given target to determine the location to retrieve to. */
2009-09-26 08:18:04 +02:00
private def retrieve(eventManager: EventManager, module: ModuleDescriptor, target: UpdateTarget.Value)
2009-08-21 14:12:43 +02:00
{
val retrieveOptions = new RetrieveOptions
val retrieveEngine = new RetrieveEngine(settings, eventManager)
val pattern =
target match
{
2009-09-26 08:18:04 +02:00
// see BootConfiguration
case UpdateApp => appRetrievePattern(config.app.toID)
2009-08-21 14:12:43 +02:00
case UpdateScala => scalaRetrievePattern
}
2009-09-26 08:18:04 +02:00
retrieveEngine.retrieve(module.getModuleRevisionId, baseDirectoryName(scalaVersion) + "/" + pattern, retrieveOptions)
2009-08-21 14:12:43 +02:00
}
/** Add the scala tools repositories and a URL resolver to download sbt from the Google code project.*/
2009-09-26 08:18:04 +02:00
private def addResolvers(settings: IvySettings)
2009-08-21 14:12:43 +02:00
{
val newDefault = new ChainResolver
newDefault.setName("redefined-public")
2009-09-26 08:18:04 +02:00
if(config.repositories.isEmpty) throw new BootException("No repositories defined.")
config.repositories.foreach(repo => newDefault.add(toIvyRepository(settings, repo)))
2009-08-21 14:12:43 +02:00
onDefaultRepositoryCacheManager(settings)(_.setUseOrigin(true))
settings.addResolver(newDefault)
settings.setDefaultResolver(newDefault.getName)
}
2009-09-26 08:18:04 +02:00
private def toIvyRepository(settings: IvySettings, repo: Repository) =
{
import Repository.{Ivy, Maven, Predefined}
import Predefined._
repo match
{
case Maven(id, url) => mavenResolver(id, url)
case Ivy(id, url, pattern) => urlResolver(id, url, 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")
}
}
2009-08-21 14:12:43 +02:00
private def onDefaultRepositoryCacheManager(settings: IvySettings)(f: DefaultRepositoryCacheManager => Unit)
{
settings.getDefaultRepositoryCacheManager match
{
case manager: DefaultRepositoryCacheManager => f(manager)
case _ => ()
}
}
/** Uses the pattern defined in BuildConfiguration to download sbt from Google code.*/
2009-09-26 08:18:04 +02:00
private def urlResolver(id: String, base: String, pattern: String) =
2009-08-21 14:12:43 +02:00
{
val resolver = new URLResolver
2009-09-26 08:18:04 +02:00
resolver.setName(id)
val adjusted = (if(base.endsWith("/")) base else (base + "/") ) + pattern
resolver.addIvyPattern(adjusted)
resolver.addArtifactPattern(adjusted)
2009-08-21 14:12:43 +02:00
resolver
}
private def mavenLocal = mavenResolver("Maven2 Local", "file://" + System.getProperty("user.home") + "/.m2/repository/")
/** Creates a maven-style resolver.*/
private def mavenResolver(name: String, root: String) =
{
val resolver = defaultMavenResolver(name)
resolver.setRoot(root)
resolver
}
/** Creates a resolver for Maven Central.*/
private def mavenMainResolver = defaultMavenResolver("Maven Central")
/** Creates a maven-style resolver with the default root.*/
private def defaultMavenResolver(name: String) =
{
val resolver = new IBiblioResolver
resolver.setName(name)
resolver.setM2compatible(true)
resolver
}
private def localResolver(ivyUserDirectory: String) =
{
val localIvyRoot = ivyUserDirectory + "/local"
val resolver = new FileSystemResolver
resolver.setName(LocalIvyName)
2009-09-26 08:18:04 +02:00
resolver.addIvyPattern(localIvyRoot + "/" + LocalIvyPattern)
resolver.addArtifactPattern(localIvyRoot + "/" + LocalArtifactPattern)
2009-08-21 14:12:43 +02:00
resolver
}
/** Logs the given message to a file and to the console. */
private def log(msg: String) =
{
try { logWriter.println(msg) }
catch { case e: Exception => System.err.println("Error writing to update log file: " + e.toString) }
println(msg)
}
}
/** A custom logger for Ivy to ignore the messages about not finding classes
* intentionally filtered using proguard. */
private final class SbtIvyLogger(logWriter: PrintWriter) extends DefaultMessageLogger(Message.MSG_INFO) with NotNull
{
private val ignorePrefix = "impossible to define"
override def log(msg: String, level: Int)
{
logWriter.println(msg)
if(level <= getLevel && msg != null && !msg.startsWith(ignorePrefix))
System.out.println(msg)
}
override def rawlog(msg: String, level: Int) { log(msg, level) }
}