mirror of https://github.com/sbt/sbt.git
227 lines
8.8 KiB
Scala
227 lines
8.8 KiB
Scala
|
|
/* 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
|
||
|
|
import core.module.descriptor.{Configuration, DefaultDependencyDescriptor, DefaultModuleDescriptor, ModuleDescriptor}
|
||
|
|
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._
|
||
|
|
|
||
|
|
private object UpdateTarget extends Enumeration
|
||
|
|
{
|
||
|
|
val UpdateScala, UpdateSbt = Value
|
||
|
|
}
|
||
|
|
import UpdateTarget.{UpdateSbt, UpdateScala}
|
||
|
|
|
||
|
|
/** Ensures that the Scala and sbt jars exist for the given versions or else downloads them.*/
|
||
|
|
final class Update(bootDirectory: File, sbtVersion: String, scalaVersion: String)
|
||
|
|
{
|
||
|
|
private def logFile = new File(bootDirectory, UpdateLogName)
|
||
|
|
/** A Writer to use to write the full logging information to a file for debugging. **/
|
||
|
|
private lazy val logWriter =
|
||
|
|
{
|
||
|
|
bootDirectory.mkdirs
|
||
|
|
new PrintWriter(new FileWriter(logFile))
|
||
|
|
}
|
||
|
|
|
||
|
|
/** 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))
|
||
|
|
try { update(target) }
|
||
|
|
catch
|
||
|
|
{
|
||
|
|
case e: Exception =>
|
||
|
|
e.printStackTrace(logWriter)
|
||
|
|
log(e.toString)
|
||
|
|
println(" (see " + logFile + " for complete log)")
|
||
|
|
}
|
||
|
|
finally { logWriter.close() }
|
||
|
|
}
|
||
|
|
/** Runs update for the specified target (updates either the scala or sbt jars for building the project) */
|
||
|
|
private def update(target: UpdateTarget.Value)
|
||
|
|
{
|
||
|
|
val settings = new IvySettings
|
||
|
|
val ivy = Ivy.newInstance(settings)
|
||
|
|
ivy.pushContext()
|
||
|
|
try { update(target, settings) }
|
||
|
|
finally { ivy.popContext() }
|
||
|
|
}
|
||
|
|
private def update(target: UpdateTarget.Value, settings: IvySettings)
|
||
|
|
{
|
||
|
|
import Configuration.Visibility.PUBLIC
|
||
|
|
// the actual module id here is not that important
|
||
|
|
val moduleID = new DefaultModuleDescriptor(createID(SbtOrg, "boot", "1.0"), "release", null, false)
|
||
|
|
moduleID.setLastModified(System.currentTimeMillis)
|
||
|
|
moduleID.addConfiguration(new Configuration(DefaultIvyConfiguration, PUBLIC, "", Array(), true, null))
|
||
|
|
// add dependencies based on which target needs updating
|
||
|
|
target match
|
||
|
|
{
|
||
|
|
case UpdateScala =>
|
||
|
|
addDependency(moduleID, ScalaOrg, CompilerModuleName, scalaVersion)
|
||
|
|
addDependency(moduleID, ScalaOrg, LibraryModuleName, scalaVersion)
|
||
|
|
update(settings, moduleID, target)
|
||
|
|
case UpdateSbt =>
|
||
|
|
addDependency(moduleID, SbtOrg, SbtModuleName + "_" + scalaVersion, sbtVersion)
|
||
|
|
update(settings, moduleID, target)
|
||
|
|
}
|
||
|
|
}
|
||
|
|
/** Runs the resolve and retrieve for the given moduleID, which has had its dependencies added already. */
|
||
|
|
private def update(settings: IvySettings, moduleID: DefaultModuleDescriptor, target: UpdateTarget.Value)
|
||
|
|
{
|
||
|
|
val eventManager = new EventManager
|
||
|
|
addResolvers(settings, scalaVersion, target)
|
||
|
|
settings.setDefaultConflictManager(settings.getConflictManager(ConflictManagerName))
|
||
|
|
settings.setBaseDir(bootDirectory)
|
||
|
|
resolve(settings, eventManager, moduleID)
|
||
|
|
retrieve(settings, eventManager, moduleID, target)
|
||
|
|
}
|
||
|
|
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)
|
||
|
|
{
|
||
|
|
val dep = new DefaultDependencyDescriptor(moduleID, createID(organization, name, revision), false, false, true)
|
||
|
|
dep.addDependencyConfiguration(DefaultIvyConfiguration, "default")
|
||
|
|
moduleID.addDependency(dep)
|
||
|
|
}
|
||
|
|
private def resolve(settings: IvySettings, eventManager: EventManager, module: ModuleDescriptor)
|
||
|
|
{
|
||
|
|
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. */
|
||
|
|
private def retrieve(settings: IvySettings, eventManager: EventManager, module: ModuleDescriptor, target: UpdateTarget.Value)
|
||
|
|
{
|
||
|
|
val retrieveOptions = new RetrieveOptions
|
||
|
|
val retrieveEngine = new RetrieveEngine(settings, eventManager)
|
||
|
|
val pattern =
|
||
|
|
target match
|
||
|
|
{
|
||
|
|
// see BuildConfiguration
|
||
|
|
case UpdateSbt => sbtRetrievePattern(sbtVersion)
|
||
|
|
case UpdateScala => scalaRetrievePattern
|
||
|
|
}
|
||
|
|
retrieveEngine.retrieve(module.getModuleRevisionId, pattern, retrieveOptions);
|
||
|
|
}
|
||
|
|
/** Add the scala tools repositories and a URL resolver to download sbt from the Google code project.*/
|
||
|
|
private def addResolvers(settings: IvySettings, scalaVersion: String, target: UpdateTarget.Value)
|
||
|
|
{
|
||
|
|
val newDefault = new ChainResolver
|
||
|
|
newDefault.setName("redefined-public")
|
||
|
|
newDefault.add(localResolver(settings.getDefaultIvyUserDir.getAbsolutePath))
|
||
|
|
newDefault.add(mavenLocal)
|
||
|
|
newDefault.add(mavenMainResolver)
|
||
|
|
target match
|
||
|
|
{
|
||
|
|
case UpdateSbt =>
|
||
|
|
newDefault.add(sbtResolver(scalaVersion))
|
||
|
|
case UpdateScala =>
|
||
|
|
newDefault.add(mavenResolver("Scala-Tools Maven2 Repository", "http://scala-tools.org/repo-releases"))
|
||
|
|
newDefault.add(mavenResolver("Scala-Tools Maven2 Snapshots Repository", "http://scala-tools.org/repo-snapshots"))
|
||
|
|
}
|
||
|
|
onDefaultRepositoryCacheManager(settings)(_.setUseOrigin(true))
|
||
|
|
settings.addResolver(newDefault)
|
||
|
|
settings.setDefaultResolver(newDefault.getName)
|
||
|
|
}
|
||
|
|
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.*/
|
||
|
|
private def sbtResolver(scalaVersion: String) =
|
||
|
|
{
|
||
|
|
val pattern = sbtResolverPattern(scalaVersion)
|
||
|
|
val resolver = new URLResolver
|
||
|
|
resolver.setName("Sbt Repository")
|
||
|
|
resolver.addIvyPattern(pattern)
|
||
|
|
resolver.addArtifactPattern(pattern)
|
||
|
|
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 artifactPattern = localIvyRoot + "/" + LocalArtifactPattern
|
||
|
|
val ivyPattern = localIvyRoot + "/" + LocalIvyPattern
|
||
|
|
val resolver = new FileSystemResolver
|
||
|
|
resolver.setName(LocalIvyName)
|
||
|
|
resolver.addIvyPattern(ivyPattern)
|
||
|
|
resolver.addArtifactPattern(artifactPattern)
|
||
|
|
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) }
|
||
|
|
}
|