mirror of https://github.com/sbt/sbt.git
Use lock file to control access to boot directory during update
This commit is contained in:
parent
0a79f1ebbd
commit
92fc01f185
|
|
@ -72,10 +72,12 @@ final class RunConfiguration(val scalaVersion: String, val app: xsbti.Applicatio
|
||||||
import BootConfiguration.{appDirectoryName, baseDirectoryName, ScalaDirectoryName, TestLoadScalaClasses}
|
import BootConfiguration.{appDirectoryName, baseDirectoryName, ScalaDirectoryName, TestLoadScalaClasses}
|
||||||
class Launch(val bootDirectory: File, repositories: List[Repository]) extends xsbti.Launcher
|
class Launch(val bootDirectory: File, repositories: List[Repository]) extends xsbti.Launcher
|
||||||
{
|
{
|
||||||
|
bootDirectory.mkdirs
|
||||||
private val scalaProviders = new Cache[String, ScalaProvider](new ScalaProvider(_))
|
private val scalaProviders = new Cache[String, ScalaProvider](new ScalaProvider(_))
|
||||||
def getScala(version: String): xsbti.ScalaProvider = scalaProviders(version)
|
def getScala(version: String): xsbti.ScalaProvider = scalaProviders(version)
|
||||||
|
|
||||||
lazy val topLoader = new BootFilteredLoader(getClass.getClassLoader)
|
lazy val topLoader = new BootFilteredLoader(getClass.getClassLoader)
|
||||||
|
val updateLockFile = new File(bootDirectory, "sbt.boot.lock")
|
||||||
|
|
||||||
def globalLock: xsbti.GlobalLock = Locks
|
def globalLock: xsbti.GlobalLock = Locks
|
||||||
|
|
||||||
|
|
@ -93,6 +95,7 @@ class Launch(val bootDirectory: File, repositories: List[Repository]) extends xs
|
||||||
def testLoadClasses = TestLoadScalaClasses
|
def testLoadClasses = TestLoadScalaClasses
|
||||||
def target = UpdateScala
|
def target = UpdateScala
|
||||||
def failLabel = "Scala " + version
|
def failLabel = "Scala " + version
|
||||||
|
def lockFile = updateLockFile
|
||||||
|
|
||||||
def app(id: xsbti.ApplicationID): xsbti.AppProvider = new AppProvider(id)
|
def app(id: xsbti.ApplicationID): xsbti.AppProvider = new AppProvider(id)
|
||||||
|
|
||||||
|
|
@ -106,6 +109,7 @@ class Launch(val bootDirectory: File, repositories: List[Repository]) extends xs
|
||||||
def testLoadClasses = List(id.mainClass)
|
def testLoadClasses = List(id.mainClass)
|
||||||
def target = new UpdateApp(Application(id))
|
def target = new UpdateApp(Application(id))
|
||||||
def failLabel = id.name + " " + id.version
|
def failLabel = id.name + " " + id.version
|
||||||
|
def lockFile = updateLockFile
|
||||||
|
|
||||||
lazy val mainClass: Class[T] forSome { type T <: xsbti.AppMain } =
|
lazy val mainClass: Class[T] forSome { type T <: xsbti.AppMain } =
|
||||||
{
|
{
|
||||||
|
|
@ -121,7 +125,7 @@ class Launch(val bootDirectory: File, repositories: List[Repository]) extends xs
|
||||||
class ComponentProvider(baseDirectory: File) extends xsbti.ComponentProvider
|
class ComponentProvider(baseDirectory: File) extends xsbti.ComponentProvider
|
||||||
{
|
{
|
||||||
def componentLocation(id: String): File = new File(baseDirectory, id)
|
def componentLocation(id: String): File = new File(baseDirectory, id)
|
||||||
def component(id: String) = GetJars.wrapNull(componentLocation(id).listFiles).filter(_.isFile)
|
def component(id: String) = Provider.wrapNull(componentLocation(id).listFiles).filter(_.isFile)
|
||||||
def defineComponent(id: String, files: Array[File]) =
|
def defineComponent(id: String, files: Array[File]) =
|
||||||
{
|
{
|
||||||
val location = componentLocation(id)
|
val location = componentLocation(id)
|
||||||
|
|
|
||||||
|
|
@ -8,7 +8,7 @@ import java.util.concurrent.Callable
|
||||||
object Locks extends xsbti.GlobalLock
|
object Locks extends xsbti.GlobalLock
|
||||||
{
|
{
|
||||||
private[this] val locks = new Cache[File, GlobalLock](new GlobalLock(_))
|
private[this] val locks = new Cache[File, GlobalLock](new GlobalLock(_))
|
||||||
def apply[T](file: File, action: Callable[T]) =
|
def apply[T](file: File, action: Callable[T]): T =
|
||||||
synchronized { locks(file.getCanonicalFile).withLock(action) }
|
synchronized { locks(file.getCanonicalFile).withLock(action) }
|
||||||
|
|
||||||
private[this] class GlobalLock(file: File)
|
private[this] class GlobalLock(file: File)
|
||||||
|
|
|
||||||
|
|
@ -3,6 +3,7 @@ package xsbt.boot
|
||||||
import Pre._
|
import Pre._
|
||||||
import java.io.{File, FileFilter}
|
import java.io.{File, FileFilter}
|
||||||
import java.net.{URL, URLClassLoader}
|
import java.net.{URL, URLClassLoader}
|
||||||
|
import java.util.concurrent.Callable
|
||||||
|
|
||||||
trait Provider extends NotNull
|
trait Provider extends NotNull
|
||||||
{
|
{
|
||||||
|
|
@ -12,39 +13,41 @@ trait Provider extends NotNull
|
||||||
def target: UpdateTarget
|
def target: UpdateTarget
|
||||||
def failLabel: String
|
def failLabel: String
|
||||||
def parentLoader: ClassLoader
|
def parentLoader: ClassLoader
|
||||||
|
def lockFile: File
|
||||||
|
|
||||||
val (jars, loader) =
|
val (jars, loader) = Locks(lockFile, new initialize)
|
||||||
|
private final class initialize extends Callable[(Array[File], ClassLoader)]
|
||||||
{
|
{
|
||||||
val (existingJars, existingLoader) = createLoader
|
def call =
|
||||||
if(Check.needsUpdate(existingLoader, testLoadClasses))
|
|
||||||
{
|
{
|
||||||
( new Update(configuration) )(target)
|
val (existingJars, existingLoader) = createLoader
|
||||||
val (newJars, newLoader) = createLoader
|
if(Provider.needsUpdate(existingLoader, testLoadClasses))
|
||||||
Check.failIfMissing(newLoader, testLoadClasses, failLabel)
|
{
|
||||||
(newJars, newLoader)
|
( new Update(configuration) )(target)
|
||||||
|
val (newJars, newLoader) = createLoader
|
||||||
|
Provider.failIfMissing(newLoader, testLoadClasses, failLabel)
|
||||||
|
(newJars, newLoader)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
(existingJars, existingLoader)
|
||||||
|
}
|
||||||
|
def createLoader =
|
||||||
|
{
|
||||||
|
val jars = Provider.getJars(baseDirectories)
|
||||||
|
(jars, new URLClassLoader(jars.map(_.toURI.toURL), parentLoader) )
|
||||||
}
|
}
|
||||||
else
|
|
||||||
(existingJars, existingLoader)
|
|
||||||
}
|
|
||||||
def createLoader =
|
|
||||||
{
|
|
||||||
val jars = GetJars(baseDirectories)
|
|
||||||
(jars, new URLClassLoader(jars.map(_.toURI.toURL), parentLoader) )
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
object GetJars
|
object Provider
|
||||||
{
|
{
|
||||||
def apply(directories: List[File]): Array[File] = toArray(directories.flatMap(directory => wrapNull(directory.listFiles(JarFilter))))
|
def getJars(directories: List[File]): Array[File] = toArray(directories.flatMap(directory => wrapNull(directory.listFiles(JarFilter))))
|
||||||
def wrapNull(a: Array[File]): Array[File] = if(a == null) Array() else a
|
def wrapNull(a: Array[File]): Array[File] = if(a == null) Array() else a
|
||||||
|
|
||||||
object JarFilter extends FileFilter
|
object JarFilter extends FileFilter
|
||||||
{
|
{
|
||||||
def accept(file: File) = !file.isDirectory && file.getName.endsWith(".jar")
|
def accept(file: File) = !file.isDirectory && file.getName.endsWith(".jar")
|
||||||
}
|
}
|
||||||
}
|
|
||||||
object Check
|
|
||||||
{
|
|
||||||
def failIfMissing(loader: ClassLoader, classes: Iterable[String], label: String) =
|
def failIfMissing(loader: ClassLoader, classes: Iterable[String], label: String) =
|
||||||
checkTarget(loader, classes, (), missing => error("Could not retrieve " + label + ": missing " + missing.mkString(", ")))
|
checkTarget(loader, classes, (), missing => error("Could not retrieve " + label + ": missing " + missing.mkString(", ")))
|
||||||
def needsUpdate(loader: ClassLoader, classes: Iterable[String]) = checkTarget(loader, classes, false, x => true)
|
def needsUpdate(loader: ClassLoader, classes: Iterable[String]) = checkTarget(loader, classes, false, x => true)
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue