2010-02-08 05:45:19 +01:00
|
|
|
/* sbt -- Simple Build Tool
|
|
|
|
|
* Copyright 2008, 2009, 2010 Mark Harrah
|
|
|
|
|
*/
|
2010-01-16 01:05:23 +01:00
|
|
|
package sbt
|
2009-08-18 06:51:08 +02:00
|
|
|
|
2009-10-11 04:01:03 +02:00
|
|
|
import java.io.{File,FileOutputStream}
|
2009-10-15 00:05:53 +02:00
|
|
|
import java.util.concurrent.Callable
|
2009-08-18 06:51:08 +02:00
|
|
|
|
|
|
|
|
/** A component manager provides access to the pieces of xsbt that are distributed as components.
|
|
|
|
|
* There are two types of components. The first type is compiled subproject jars with their dependencies.
|
|
|
|
|
* The second type is a subproject distributed as a source jar so that it can be compiled against a specific
|
|
|
|
|
* version of Scala.
|
|
|
|
|
*
|
|
|
|
|
* The component manager provides services to install and retrieve components to the local repository.
|
2009-08-20 06:02:06 +02:00
|
|
|
* This is used for compiled source jars so that the compilation need not be repeated for other projects on the same
|
2009-08-18 06:51:08 +02:00
|
|
|
* machine.
|
|
|
|
|
*/
|
2010-09-04 14:19:58 +02:00
|
|
|
class ComponentManager(globalLock: xsbti.GlobalLock, provider: xsbti.ComponentProvider, val log: Logger) extends NotNull
|
2009-08-18 06:51:08 +02:00
|
|
|
{
|
2009-08-20 06:02:06 +02:00
|
|
|
/** Get all of the files for component 'id', throwing an exception if no files exist for the component. */
|
2009-10-11 04:01:03 +02:00
|
|
|
def files(id: String)(ifMissing: IfMissing): Iterable[File] =
|
2009-08-18 06:51:08 +02:00
|
|
|
{
|
2009-10-11 04:01:03 +02:00
|
|
|
def fromGlobal =
|
|
|
|
|
lockGlobalCache {
|
|
|
|
|
try { update(id); getOrElse(createAndCache) }
|
|
|
|
|
catch { case e: NotInCache => createAndCache }
|
|
|
|
|
}
|
2010-06-16 02:38:18 +02:00
|
|
|
def getOrElse(orElse: => Iterable[File]): Iterable[File] =
|
2009-10-11 04:01:03 +02:00
|
|
|
{
|
|
|
|
|
val existing = provider.component(id)
|
|
|
|
|
if(existing.isEmpty) orElse else existing
|
|
|
|
|
}
|
|
|
|
|
def notFound = invalid("Could not find required component '" + id + "'")
|
|
|
|
|
def createAndCache =
|
|
|
|
|
ifMissing match {
|
|
|
|
|
case IfMissing.Fail => notFound
|
|
|
|
|
case d: IfMissing.Define =>
|
|
|
|
|
d()
|
|
|
|
|
if(d.cache) cache(id)
|
|
|
|
|
getOrElse(notFound)
|
|
|
|
|
}
|
2009-10-15 00:05:53 +02:00
|
|
|
|
2009-10-11 04:01:03 +02:00
|
|
|
lockLocalCache { getOrElse(fromGlobal) }
|
2009-08-18 06:51:08 +02:00
|
|
|
}
|
2010-01-29 01:31:04 +01:00
|
|
|
/** This is used to lock the local cache in project/boot/. By checking the local cache first, we can avoid grabbing a global lock. */
|
2009-10-15 00:05:53 +02:00
|
|
|
private def lockLocalCache[T](action: => T): T = lock(provider.lockFile)( action )
|
2010-01-29 01:31:04 +01:00
|
|
|
/** This is used to ensure atomic access to components in the global Ivy cache.*/
|
2009-10-15 00:05:53 +02:00
|
|
|
private def lockGlobalCache[T](action: => T): T = lock(IvyCache.lockFile)( action )
|
|
|
|
|
private def lock[T](file: File)(action: => T): T = globalLock(file, new Callable[T] { def call = action })
|
2009-08-20 06:02:06 +02:00
|
|
|
/** Get the file for component 'id', throwing an exception if no files or multiple files exist for the component. */
|
2009-10-11 04:01:03 +02:00
|
|
|
def file(id: String)(ifMissing: IfMissing): File =
|
|
|
|
|
files(id)(ifMissing).toList match {
|
2009-08-18 06:51:08 +02:00
|
|
|
case x :: Nil => x
|
2009-08-20 06:02:06 +02:00
|
|
|
case xs => invalid("Expected single file for component '" + id + "', found: " + xs.mkString(", "))
|
2009-08-18 06:51:08 +02:00
|
|
|
}
|
2009-08-20 06:02:06 +02:00
|
|
|
private def invalid(msg: String) = throw new InvalidComponent(msg)
|
|
|
|
|
private def invalid(e: NotInCache) = throw new InvalidComponent(e.getMessage, e)
|
|
|
|
|
|
2009-10-11 04:01:03 +02:00
|
|
|
def define(id: String, files: Iterable[File]) = lockLocalCache { provider.defineComponent(id, files.toSeq.toArray) }
|
2009-08-20 06:02:06 +02:00
|
|
|
/** Retrieve the file for component 'id' from the local repository. */
|
2010-01-29 01:31:04 +01:00
|
|
|
private def update(id: String): Unit = IvyCache.withCachedJar(sbtModuleID(id), Some(globalLock), log)(jar => define(id, Seq(jar)) )
|
2009-08-20 06:02:06 +02:00
|
|
|
|
2009-11-10 04:02:53 +01:00
|
|
|
private def sbtModuleID(id: String) = ModuleID("org.scala-tools.sbt", id, ComponentManager.stampedVersion)
|
2009-08-20 06:02:06 +02:00
|
|
|
/** Install the files for component 'id' to the local repository. This is usually used after writing files to the directory returned by 'location'. */
|
2010-01-29 01:31:04 +01:00
|
|
|
def cache(id: String): Unit = IvyCache.cacheJar(sbtModuleID(id), file(id)(IfMissing.Fail), Some(globalLock), log)
|
|
|
|
|
def clearCache(id: String): Unit = lockGlobalCache { IvyCache.clearCachedJar(sbtModuleID(id), Some(globalLock), log) }
|
2009-08-20 06:02:06 +02:00
|
|
|
}
|
|
|
|
|
class InvalidComponent(msg: String, cause: Throwable) extends RuntimeException(msg, cause)
|
|
|
|
|
{
|
|
|
|
|
def this(msg: String) = this(msg, null)
|
2009-10-11 04:01:03 +02:00
|
|
|
}
|
|
|
|
|
sealed trait IfMissing extends NotNull
|
|
|
|
|
object IfMissing
|
|
|
|
|
{
|
|
|
|
|
object Fail extends IfMissing
|
|
|
|
|
final class Define(val cache: Boolean, define: => Unit) extends IfMissing { def apply() = define }
|
2009-11-10 04:02:53 +01:00
|
|
|
}
|
|
|
|
|
object ComponentManager
|
|
|
|
|
{
|
|
|
|
|
lazy val (version, timestamp) =
|
|
|
|
|
{
|
|
|
|
|
val properties = new java.util.Properties
|
2010-03-08 01:07:52 +01:00
|
|
|
val propertiesStream = versionResource.openStream
|
2009-11-10 04:02:53 +01:00
|
|
|
try { properties.load(propertiesStream) } finally { propertiesStream.close() }
|
|
|
|
|
(properties.getProperty("version"), properties.getProperty("timestamp"))
|
|
|
|
|
}
|
|
|
|
|
lazy val stampedVersion = version + "_" + timestamp
|
2010-03-08 01:07:52 +01:00
|
|
|
|
|
|
|
|
import java.net.URL
|
|
|
|
|
private def versionResource: URL =
|
|
|
|
|
{
|
|
|
|
|
import java.util.Collections.list
|
|
|
|
|
// could be just:
|
|
|
|
|
// getClass.getResource("/xsbt.version.properties")
|
|
|
|
|
// but the launcher up until 0.7.1 contained a stale xsbt.version.properties.
|
|
|
|
|
// post-0.7.1, only xsbti.jar contains xsbt.version.properties
|
|
|
|
|
// for the transition, we take the last one, which is the one we want because of the order resources are searched
|
|
|
|
|
list(getClass.getClassLoader.getResources("xsbt.version.properties")).toArray(new Array[URL](0)).last
|
|
|
|
|
}
|
2009-08-18 06:51:08 +02:00
|
|
|
}
|