mirror of https://github.com/sbt/sbt.git
Tests and fixes for component manager and cache interface.
This commit is contained in:
parent
a70ddd8e32
commit
108807a773
|
|
@ -23,7 +23,7 @@ class Compiler(scalaLoader: ClassLoader, val scalaVersion: String, private[xsbt]
|
||||||
// The following import ensures there is a compile error if the class name changes,
|
// The following import ensures there is a compile error if the class name changes,
|
||||||
// but it should not be otherwise directly referenced
|
// but it should not be otherwise directly referenced
|
||||||
import scala.tools.nsc.Main
|
import scala.tools.nsc.Main
|
||||||
|
|
||||||
val mainClass = Class.forName("scala.tools.nsc.Main", true, scalaLoader)
|
val mainClass = Class.forName("scala.tools.nsc.Main", true, scalaLoader)
|
||||||
val main = mainClass.asInstanceOf[{def process(args: Array[String]): Unit }]
|
val main = mainClass.asInstanceOf[{def process(args: Array[String]): Unit }]
|
||||||
main.process(arguments.toArray)
|
main.process(arguments.toArray)
|
||||||
|
|
@ -41,7 +41,9 @@ class Compiler(scalaLoader: ClassLoader, val scalaVersion: String, private[xsbt]
|
||||||
val interfaceLoader = new URLClassLoader(Array(interfaceJar.toURI.toURL), scalaLoader)
|
val interfaceLoader = new URLClassLoader(Array(interfaceJar.toURI.toURL), scalaLoader)
|
||||||
val interface = Class.forName("xsbt.CompilerInterface", true, interfaceLoader).newInstance
|
val interface = Class.forName("xsbt.CompilerInterface", true, interfaceLoader).newInstance
|
||||||
val runnable = interface.asInstanceOf[{ def run(args: Array[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger): Unit }]
|
val runnable = interface.asInstanceOf[{ def run(args: Array[String], callback: AnalysisCallback, maximumErrors: Int, log: Logger): Unit }]
|
||||||
runnable.run(argsWithPlugin.toArray, callback, maximumErrors, log) // safe to pass across the ClassLoader boundary because the types are defined in Java
|
// these arguments are safe to pass across the ClassLoader boundary because the types are defined in Java
|
||||||
|
// so they will be binary compatible across all versions of Scala
|
||||||
|
runnable.run(argsWithPlugin.toArray, callback, maximumErrors, log)
|
||||||
}
|
}
|
||||||
def forceInitialization() {interfaceJar }
|
def forceInitialization() {interfaceJar }
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -11,7 +11,7 @@ object CheckBasic extends Specification
|
||||||
"Compiling basic file should succeed" in {
|
"Compiling basic file should succeed" in {
|
||||||
WithFiles(basicName -> basicSource){ files =>
|
WithFiles(basicName -> basicSource){ files =>
|
||||||
TestCompile(files){ loader => Class.forName("org.example.Basic", false, loader) }
|
TestCompile(files){ loader => Class.forName("org.example.Basic", false, loader) }
|
||||||
true must be(true) // don't know how to just check that previous line completes without exception
|
true must beTrue // don't know how to just check that previous line completes without exception
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
"Analyzer plugin should send source begin and end" in {
|
"Analyzer plugin should send source begin and end" in {
|
||||||
|
|
|
||||||
|
|
@ -8,10 +8,16 @@ class TestLogger extends Logger
|
||||||
def debug(msg: F0[String]) = buffer("[debug] ", msg)
|
def debug(msg: F0[String]) = buffer("[debug] ", msg)
|
||||||
def error(msg: F0[String]) = buffer("[error] ", msg)
|
def error(msg: F0[String]) = buffer("[error] ", msg)
|
||||||
def verbose(msg: F0[String]) = buffer("[verbose] ", msg)
|
def verbose(msg: F0[String]) = buffer("[verbose] ", msg)
|
||||||
|
def info(msg: => String) = buffer("[info] ", msg)
|
||||||
|
def warn(msg: => String) = buffer("[warn] ", msg)
|
||||||
|
def debug(msg: => String) = buffer("[debug] ", msg)
|
||||||
|
def error(msg: => String) = buffer("[error] ", msg)
|
||||||
|
def verbose(msg: => String) = buffer("[verbose] ", msg)
|
||||||
def show() { buffer.foreach(_()) }
|
def show() { buffer.foreach(_()) }
|
||||||
def clear() { buffer.clear() }
|
def clear() { buffer.clear() }
|
||||||
def trace(t: F0[Throwable]) { buffer += f0(t().printStackTrace) }
|
def trace(t: F0[Throwable]) { buffer += f0(t().printStackTrace) }
|
||||||
private def buffer(s: String, msg: F0[String]) { buffer += f0(println(s + msg())) }
|
private def buffer(s: String, msg: F0[String]) { buffer(s, msg()) }
|
||||||
|
private def buffer(s: String, msg: => String) { buffer += f0(println(s + msg)) }
|
||||||
}
|
}
|
||||||
object TestLogger
|
object TestLogger
|
||||||
{
|
{
|
||||||
|
|
@ -22,4 +28,11 @@ object TestLogger
|
||||||
catch { case e: Exception => log.show(); throw e }
|
catch { case e: Exception => log.show(); throw e }
|
||||||
finally { log.clear() }
|
finally { log.clear() }
|
||||||
}
|
}
|
||||||
|
def apply[L <: TestLogger, T](newLogger: => L)(f: L => T): T =
|
||||||
|
{
|
||||||
|
val log = newLogger
|
||||||
|
try { f(log) }
|
||||||
|
catch { case e: Exception => log.show(); throw e }
|
||||||
|
finally { log.clear() }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -9,12 +9,16 @@ import xsbti.Versions
|
||||||
* version of Scala.
|
* version of Scala.
|
||||||
*
|
*
|
||||||
* The component manager provides services to install and retrieve components to the local repository.
|
* The component manager provides services to install and retrieve components to the local repository.
|
||||||
* This is used for source jars so that the compilation need not be repeated for other projects on the same
|
* This is used for compiled source jars so that the compilation need not be repeated for other projects on the same
|
||||||
* machine.
|
* machine.
|
||||||
*/
|
*/
|
||||||
class ComponentManager(baseDirectory: File, log: IvyLogger) extends NotNull
|
class ComponentManager(baseDirectory: File, log: IvyLogger) extends NotNull
|
||||||
{
|
{
|
||||||
|
/** Get the location where files for component 'id' are stored. This method does not ensure that the component is retrieved from the
|
||||||
|
* local repository. By default, the location returned is is baseDirectory / id.*/
|
||||||
def location(id: String): File = new File(baseDirectory, id)
|
def location(id: String): File = new File(baseDirectory, id)
|
||||||
|
/** Get the location where files for component 'id' are stored. If the component has not yet been retrieved from the local repository,
|
||||||
|
* it is retrieved first. */
|
||||||
def directory(id: String): File =
|
def directory(id: String): File =
|
||||||
{
|
{
|
||||||
val dir = location(id)
|
val dir = location(id)
|
||||||
|
|
@ -22,24 +26,38 @@ class ComponentManager(baseDirectory: File, log: IvyLogger) extends NotNull
|
||||||
update(id)
|
update(id)
|
||||||
dir
|
dir
|
||||||
}
|
}
|
||||||
|
// get the contents of the given directory, wrapping a null result in an empty list.
|
||||||
private def contents(dir: File): Seq[File] =
|
private def contents(dir: File): Seq[File] =
|
||||||
{
|
{
|
||||||
val fs = dir.listFiles
|
val fs = dir.listFiles
|
||||||
if(fs == null) Nil else fs
|
if(fs == null) Nil else fs
|
||||||
}
|
}
|
||||||
|
/** Get all of the files for component 'id', throwing an exception if no files exist for the component. */
|
||||||
def files(id: String): Iterable[File] =
|
def files(id: String): Iterable[File] =
|
||||||
{
|
{
|
||||||
val fs = contents(directory(id))
|
val fs = contents(directory(id))
|
||||||
if(!fs.isEmpty) fs else error("Could not find required component '" + id + "'")
|
if(!fs.isEmpty) fs else invalid("Could not find required component '" + id + "'")
|
||||||
}
|
}
|
||||||
|
/** Get the file for component 'id', throwing an exception if no files or multiple files exist for the component. */
|
||||||
def file(id: String): File =
|
def file(id: String): File =
|
||||||
files(id).toList match {
|
files(id).toList match {
|
||||||
case x :: Nil => x
|
case x :: Nil => x
|
||||||
case xs => error("Expected single file for component '" + id + "', found: " + xs.mkString(", "))
|
case xs => invalid("Expected single file for component '" + id + "', found: " + xs.mkString(", "))
|
||||||
}
|
}
|
||||||
|
private def invalid(msg: String) = throw new InvalidComponent(msg)
|
||||||
|
private def invalid(e: NotInCache) = throw new InvalidComponent(e.getMessage, e)
|
||||||
|
|
||||||
|
/** Retrieve the file for component 'id' from the local repository. */
|
||||||
def update(id: String): Unit =
|
def update(id: String): Unit =
|
||||||
IvyActions.basicRetrieveLocal(sbtModuleID("manager"), Seq(sbtModuleID(id)), location(id), log)
|
try { IvyCache.retrieveCachedJar(sbtModuleID(id), location(id), log) }
|
||||||
|
catch { case e: NotInCache => invalid(e) }
|
||||||
|
|
||||||
def sbtModuleID(id: String) = ModuleID("org.scala-tools.sbt", id, Versions.Sbt)
|
def sbtModuleID(id: String) = ModuleID("org.scala-tools.sbt", id, Versions.Sbt)
|
||||||
def cache(id: String): Unit = IvyActions.basicPublishLocal(sbtModuleID(id), Nil, files(id), log)
|
/** Install the files for component 'id' to the local repository. This is usually used after writing files to the directory returned by 'location'. */
|
||||||
|
def cache(id: String): Unit = IvyCache.cacheJar(sbtModuleID(id), file(id), log)
|
||||||
|
def clearCache(id: String): Unit = IvyCache.clearCachedJar(sbtModuleID(id), log)
|
||||||
|
}
|
||||||
|
class InvalidComponent(msg: String, cause: Throwable) extends RuntimeException(msg, cause)
|
||||||
|
{
|
||||||
|
def this(msg: String) = this(msg, null)
|
||||||
}
|
}
|
||||||
|
|
@ -73,13 +73,13 @@ final class IvySbt(configuration: IvyConfiguration)
|
||||||
try { f(ivy) }
|
try { f(ivy) }
|
||||||
finally { ivy.popContext() }
|
finally { ivy.popContext() }
|
||||||
}
|
}
|
||||||
|
|
||||||
final class Module(val moduleConfiguration: ModuleConfiguration) extends NotNull
|
final class Module(val moduleConfiguration: ModuleConfiguration) extends NotNull
|
||||||
{
|
{
|
||||||
def logger = configuration.log
|
def logger = configuration.log
|
||||||
def withModule[T](f: (Ivy,DefaultModuleDescriptor,String) => T): T =
|
def withModule[T](f: (Ivy,DefaultModuleDescriptor,String) => T): T =
|
||||||
withIvy[T] { ivy => f(ivy, moduleDescriptor, defaultConfig) }
|
withIvy[T] { ivy => f(ivy, moduleDescriptor, defaultConfig) }
|
||||||
|
|
||||||
import moduleConfiguration._
|
import moduleConfiguration._
|
||||||
private lazy val (moduleDescriptor: DefaultModuleDescriptor, defaultConfig: String) =
|
private lazy val (moduleDescriptor: DefaultModuleDescriptor, defaultConfig: String) =
|
||||||
{
|
{
|
||||||
|
|
@ -97,7 +97,7 @@ final class IvySbt(configuration: IvyConfiguration)
|
||||||
val moduleID = newConfiguredModuleID
|
val moduleID = newConfiguredModuleID
|
||||||
val defaultConf = defaultConfiguration getOrElse Configurations.config(ModuleDescriptor.DEFAULT_CONFIGURATION)
|
val defaultConf = defaultConfiguration getOrElse Configurations.config(ModuleDescriptor.DEFAULT_CONFIGURATION)
|
||||||
log.debug("Using inline dependencies specified in Scala" + (if(ivyXML.isEmpty) "." else " and XML."))
|
log.debug("Using inline dependencies specified in Scala" + (if(ivyXML.isEmpty) "." else " and XML."))
|
||||||
|
|
||||||
val parser = IvySbt.parseIvyXML(ivy.getSettings, IvySbt.wrapped(module, ivyXML), moduleID, defaultConf.name, validate)
|
val parser = IvySbt.parseIvyXML(ivy.getSettings, IvySbt.wrapped(module, ivyXML), moduleID, defaultConf.name, validate)
|
||||||
|
|
||||||
IvySbt.addArtifacts(moduleID, artifacts)
|
IvySbt.addArtifacts(moduleID, artifacts)
|
||||||
|
|
@ -112,7 +112,7 @@ final class IvySbt(configuration: IvyConfiguration)
|
||||||
configurations.foreach(config => mod.addConfiguration(IvySbt.toIvyConfiguration(config)))
|
configurations.foreach(config => mod.addConfiguration(IvySbt.toIvyConfiguration(config)))
|
||||||
mod
|
mod
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Parses the given Maven pom 'pomFile'.*/
|
/** Parses the given Maven pom 'pomFile'.*/
|
||||||
private def readPom(pomFile: File) =
|
private def readPom(pomFile: File) =
|
||||||
{
|
{
|
||||||
|
|
@ -164,11 +164,11 @@ private object IvySbt
|
||||||
val DefaultIvyConfigFilename = "ivysettings.xml"
|
val DefaultIvyConfigFilename = "ivysettings.xml"
|
||||||
val DefaultIvyFilename = "ivy.xml"
|
val DefaultIvyFilename = "ivy.xml"
|
||||||
val DefaultMavenFilename = "pom.xml"
|
val DefaultMavenFilename = "pom.xml"
|
||||||
|
|
||||||
private def defaultIvyFile(project: File) = new File(project, DefaultIvyFilename)
|
private def defaultIvyFile(project: File) = new File(project, DefaultIvyFilename)
|
||||||
private def defaultIvyConfiguration(project: File) = new File(project, DefaultIvyConfigFilename)
|
private def defaultIvyConfiguration(project: File) = new File(project, DefaultIvyConfigFilename)
|
||||||
private def defaultPOM(project: File) = new File(project, DefaultMavenFilename)
|
private def defaultPOM(project: File) = new File(project, DefaultMavenFilename)
|
||||||
|
|
||||||
/** Sets the resolvers for 'settings' to 'resolvers'. This is done by creating a new chain and making it the default. */
|
/** Sets the resolvers for 'settings' to 'resolvers'. This is done by creating a new chain and making it the default. */
|
||||||
private def setResolvers(settings: IvySettings, resolvers: Seq[Resolver], log: IvyLogger)
|
private def setResolvers(settings: IvySettings, resolvers: Seq[Resolver], log: IvyLogger)
|
||||||
{
|
{
|
||||||
|
|
@ -207,7 +207,7 @@ private object IvySbt
|
||||||
moduleID.check()
|
moduleID.check()
|
||||||
}
|
}
|
||||||
/** Converts the given sbt module id into an Ivy ModuleRevisionId.*/
|
/** Converts the given sbt module id into an Ivy ModuleRevisionId.*/
|
||||||
private def toID(m: ModuleID) =
|
private[xsbt] def toID(m: ModuleID) =
|
||||||
{
|
{
|
||||||
import m._
|
import m._
|
||||||
ModuleRevisionId.newInstance(organization, name, revision)
|
ModuleRevisionId.newInstance(organization, name, revision)
|
||||||
|
|
@ -253,7 +253,7 @@ private object IvySbt
|
||||||
parser.parse()
|
parser.parse()
|
||||||
parser
|
parser
|
||||||
}
|
}
|
||||||
|
|
||||||
/** This method is used to add inline dependencies to the provided module. */
|
/** This method is used to add inline dependencies to the provided module. */
|
||||||
def addDependencies(moduleID: DefaultModuleDescriptor, dependencies: Iterable[ModuleID], parser: CustomXmlParser.CustomParser)
|
def addDependencies(moduleID: DefaultModuleDescriptor, dependencies: Iterable[ModuleID], parser: CustomXmlParser.CustomParser)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -6,6 +6,7 @@ import org.apache.ivy.{core, plugins, util, Ivy}
|
||||||
import core.cache.DefaultRepositoryCacheManager
|
import core.cache.DefaultRepositoryCacheManager
|
||||||
import core.LogOptions
|
import core.LogOptions
|
||||||
import core.deliver.DeliverOptions
|
import core.deliver.DeliverOptions
|
||||||
|
import core.install.InstallOptions
|
||||||
import core.module.descriptor.{DefaultArtifact, DefaultDependencyArtifactDescriptor, MDArtifact}
|
import core.module.descriptor.{DefaultArtifact, DefaultDependencyArtifactDescriptor, MDArtifact}
|
||||||
import core.module.descriptor.{DefaultDependencyDescriptor, DefaultModuleDescriptor, DependencyDescriptor, ModuleDescriptor}
|
import core.module.descriptor.{DefaultDependencyDescriptor, DefaultModuleDescriptor, DependencyDescriptor, ModuleDescriptor}
|
||||||
import core.module.id.{ArtifactId,ModuleId, ModuleRevisionId}
|
import core.module.id.{ArtifactId,ModuleId, ModuleRevisionId}
|
||||||
|
|
@ -18,33 +19,24 @@ final class UpdateConfiguration(val retrieveDirectory: File, val outputPattern:
|
||||||
|
|
||||||
object IvyActions
|
object IvyActions
|
||||||
{
|
{
|
||||||
def basicPublishLocal(moduleID: ModuleID, dependencies: Iterable[ModuleID], artifactFiles: Iterable[File], log: IvyLogger)
|
/** Installs the dependencies of the given 'module' from the resolver named 'from' to the resolver named 'to'.*/
|
||||||
|
def install(module: IvySbt#Module, from: String, to: String)
|
||||||
{
|
{
|
||||||
val artifacts = artifactFiles.map(Artifact.defaultArtifact)
|
module.withModule { (ivy, md, default) =>
|
||||||
val (ivy, local) = basicLocalIvy(log)
|
for(dependency <- md.getDependencies)
|
||||||
val module = new ivy.Module(ModuleConfiguration(moduleID, dependencies, artifacts))
|
{
|
||||||
val srcArtifactPatterns = artifactFiles.map(_.getAbsolutePath)
|
module.logger.info("Installing " + dependency)
|
||||||
publish(module, local.name, srcArtifactPatterns, None, None)
|
val options = new InstallOptions
|
||||||
}
|
options.setValidate(module.moduleConfiguration.validate)
|
||||||
def basicRetrieveLocal(moduleID: ModuleID, dependencies: Iterable[ModuleID], to: File, log: IvyLogger)
|
options.setTransitive(dependency.isTransitive)
|
||||||
{
|
ivy.install(dependency.getDependencyRevisionId, from, to, options)
|
||||||
val (ivy, local) = basicLocalIvy(log)
|
}
|
||||||
val module = new ivy.Module(ModuleConfiguration(moduleID, dependencies, Nil))
|
}
|
||||||
val up = new UpdateConfiguration(to, defaultOutputPattern, false, true)
|
|
||||||
update(module, up)
|
|
||||||
}
|
|
||||||
def defaultOutputPattern = "[artifact]-[revision](-[classifier]).[ext]"
|
|
||||||
private def basicLocalIvy(log: IvyLogger) =
|
|
||||||
{
|
|
||||||
val local = Resolver.defaultLocal
|
|
||||||
val paths = new IvyPaths(new File("."), None)
|
|
||||||
val conf = new IvyConfiguration(paths, Seq(local), log)
|
|
||||||
(new IvySbt(conf), local)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Clears the Ivy cache, as configured by 'config'. */
|
/** Clears the Ivy cache, as configured by 'config'. */
|
||||||
def cleanCache(ivy: IvySbt) = ivy.withIvy { _.getSettings.getRepositoryCacheManagers.foreach(_.clean()) }
|
def cleanCache(ivy: IvySbt) = ivy.withIvy { _.getSettings.getRepositoryCacheManagers.foreach(_.clean()) }
|
||||||
|
|
||||||
/** Creates a Maven pom from the given Ivy configuration*/
|
/** Creates a Maven pom from the given Ivy configuration*/
|
||||||
def makePom(module: IvySbt#Module, extraDependencies: Iterable[ModuleID], configurations: Option[Iterable[Configuration]], output: File)
|
def makePom(module: IvySbt#Module, extraDependencies: Iterable[ModuleID], configurations: Option[Iterable[Configuration]], output: File)
|
||||||
{
|
{
|
||||||
|
|
@ -90,7 +82,7 @@ object IvyActions
|
||||||
newModule.addDependency(translated)
|
newModule.addDependency(translated)
|
||||||
newModule
|
newModule
|
||||||
}
|
}
|
||||||
|
|
||||||
def deliver(module: IvySbt#Module, status: String, deliverIvyPattern: String, extraDependencies: Iterable[ModuleID], configurations: Option[Iterable[Configuration]], quiet: Boolean)
|
def deliver(module: IvySbt#Module, status: String, deliverIvyPattern: String, extraDependencies: Iterable[ModuleID], configurations: Option[Iterable[Configuration]], quiet: Boolean)
|
||||||
{
|
{
|
||||||
module.withModule { case (ivy, md, default) =>
|
module.withModule { case (ivy, md, default) =>
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,103 @@
|
||||||
|
package xsbt
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import java.net.URL
|
||||||
|
|
||||||
|
import org.apache.ivy.{core, plugins, util}
|
||||||
|
import core.cache.{ArtifactOrigin, CacheDownloadOptions, DefaultRepositoryCacheManager}
|
||||||
|
import core.module.descriptor.{Artifact => IvyArtifact, DefaultArtifact}
|
||||||
|
import plugins.repository.file.{FileRepository=>IvyFileRepository, FileResource}
|
||||||
|
import plugins.repository.{ArtifactResourceResolver, Resource, ResourceDownloader}
|
||||||
|
import plugins.resolver.util.ResolvedResource
|
||||||
|
import util.FileUtil
|
||||||
|
|
||||||
|
class NotInCache(val id: ModuleID, cause: Throwable)
|
||||||
|
extends RuntimeException(NotInCache(id, cause), cause)
|
||||||
|
{
|
||||||
|
def this(id: ModuleID) = this(id, null)
|
||||||
|
}
|
||||||
|
private object NotInCache
|
||||||
|
{
|
||||||
|
def apply(id: ModuleID, cause: Throwable) =
|
||||||
|
{
|
||||||
|
val postfix = if(cause == null) "" else (": " +cause.toString)
|
||||||
|
"File for " + id + " not in cache" + postfix
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Provides methods for working at the level of a single jar file with the default Ivy cache.*/
|
||||||
|
object IvyCache
|
||||||
|
{
|
||||||
|
/** Caches the given 'file' with the given ID. It may be retrieved or cleared using this ID.*/
|
||||||
|
def cacheJar(moduleID: ModuleID, file: File, log: IvyLogger)
|
||||||
|
{
|
||||||
|
val artifact = defaultArtifact(moduleID)
|
||||||
|
val resolved = new ResolvedResource(new FileResource(new IvyFileRepository, file), moduleID.revision)
|
||||||
|
withDefaultCache(log) { cache =>
|
||||||
|
val resolver = new ArtifactResourceResolver { def resolve(artifact: IvyArtifact) = resolved }
|
||||||
|
cache.download(artifact, resolver, new FileDownloader, new CacheDownloadOptions)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
/** Clears the cache of the jar for the given ID.*/
|
||||||
|
def clearCachedJar(id: ModuleID, log: IvyLogger)
|
||||||
|
{
|
||||||
|
try { getCachedFile(id, log).delete }
|
||||||
|
catch { case e: Exception => log.debug("Error cleaning cached jar: " + e.toString) }
|
||||||
|
}
|
||||||
|
/** Copies the cached jar for the given ID to the directory 'toDirectory'. If the jar is not in the cache, NotInCache is thrown.*/
|
||||||
|
def retrieveCachedJar(id: ModuleID, toDirectory: File, log: IvyLogger) =
|
||||||
|
{
|
||||||
|
val cachedFile = getCachedFile(id, log)
|
||||||
|
val copyTo = new File(toDirectory, cachedFile.getName)
|
||||||
|
FileUtil.copy(cachedFile, copyTo, null)
|
||||||
|
copyTo
|
||||||
|
}
|
||||||
|
/** Get the location of the cached jar for the given ID in the Ivy cache. If the jar is not in the cache, NotInCache is thrown.*/
|
||||||
|
def getCachedFile(id: ModuleID, log: IvyLogger): File =
|
||||||
|
{
|
||||||
|
val cachedFile =
|
||||||
|
try
|
||||||
|
{
|
||||||
|
withDefaultCache(log) { cache =>
|
||||||
|
val artifact = defaultArtifact(id)
|
||||||
|
cache.getArchiveFileInCache(artifact, unknownOrigin(artifact))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
catch { case e: Exception => throw new NotInCache(id, e) }
|
||||||
|
|
||||||
|
if(cachedFile.exists) cachedFile else throw new NotInCache(id)
|
||||||
|
}
|
||||||
|
/** Calls the given function with the default Ivy cache.*/
|
||||||
|
def withDefaultCache[T](log: IvyLogger)(f: DefaultRepositoryCacheManager => T): T =
|
||||||
|
{
|
||||||
|
val (ivy, local) = basicLocalIvy(log)
|
||||||
|
ivy.withIvy { ivy =>
|
||||||
|
val cache = ivy.getSettings.getDefaultRepositoryCacheManager.asInstanceOf[DefaultRepositoryCacheManager]
|
||||||
|
cache.setUseOrigin(false)
|
||||||
|
f(cache)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private def unknownOrigin(artifact: IvyArtifact) = ArtifactOrigin.unkwnown(artifact)
|
||||||
|
/** A minimal Ivy setup with only a local resolver and the current directory as the base directory.*/
|
||||||
|
private def basicLocalIvy(log: IvyLogger) =
|
||||||
|
{
|
||||||
|
val local = Resolver.defaultLocal
|
||||||
|
val paths = new IvyPaths(new File("."), None)
|
||||||
|
val conf = new IvyConfiguration(paths, Seq(local), log)
|
||||||
|
(new IvySbt(conf), local)
|
||||||
|
}
|
||||||
|
/** Creates a default jar artifact based on the given ID.*/
|
||||||
|
private def defaultArtifact(moduleID: ModuleID): IvyArtifact =
|
||||||
|
new DefaultArtifact(IvySbt.toID(moduleID), null, moduleID.name, "jar", "jar")
|
||||||
|
}
|
||||||
|
/** Required by Ivy for copying to the cache.*/
|
||||||
|
private class FileDownloader extends ResourceDownloader with NotNull
|
||||||
|
{
|
||||||
|
def download(artifact: IvyArtifact, resource: Resource, dest: File)
|
||||||
|
{
|
||||||
|
if(dest.exists()) dest.delete()
|
||||||
|
val part = new File(dest.getAbsolutePath + ".part")
|
||||||
|
FileUtil.copy(resource.openStream, part, null)
|
||||||
|
if(!part.renameTo(dest))
|
||||||
|
error("Could not move temporary file " + part + " to final location " + dest)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
@ -72,7 +72,7 @@ sealed abstract class PatternsBasedRepository extends Resolver
|
||||||
|
|
||||||
/** The object representing the configured patterns for this repository. */
|
/** The object representing the configured patterns for this repository. */
|
||||||
def patterns: Patterns
|
def patterns: Patterns
|
||||||
|
|
||||||
/** Enables maven 2 compatibility for this repository. */
|
/** Enables maven 2 compatibility for this repository. */
|
||||||
def mavenStyle() = copy(patterns.mavenStyle())
|
def mavenStyle() = copy(patterns.mavenStyle())
|
||||||
/** Adds the given patterns for resolving/publishing Ivy files.*/
|
/** Adds the given patterns for resolving/publishing Ivy files.*/
|
||||||
|
|
@ -100,10 +100,10 @@ sealed abstract class SshBasedRepository extends PatternsBasedRepository
|
||||||
type RepositoryType <: SshBasedRepository
|
type RepositoryType <: SshBasedRepository
|
||||||
protected def copy(connection: SshConnection): RepositoryType
|
protected def copy(connection: SshConnection): RepositoryType
|
||||||
private def copy(authentication: SshAuthentication): RepositoryType = copy(connection.copy(Some(authentication)))
|
private def copy(authentication: SshAuthentication): RepositoryType = copy(connection.copy(Some(authentication)))
|
||||||
|
|
||||||
/** The object representing the configured ssh connection for this repository. */
|
/** The object representing the configured ssh connection for this repository. */
|
||||||
def connection: SshConnection
|
def connection: SshConnection
|
||||||
|
|
||||||
/** Configures this to use the specified user name and password when connecting to the remote repository. */
|
/** Configures this to use the specified user name and password when connecting to the remote repository. */
|
||||||
def as(user: String, password: String): RepositoryType = copy(new PasswordAuthentication(user, password))
|
def as(user: String, password: String): RepositoryType = copy(new PasswordAuthentication(user, password))
|
||||||
/** Configures this to use the specified keyfile and password for the keyfile when connecting to the remote repository. */
|
/** Configures this to use the specified keyfile and password for the keyfile when connecting to the remote repository. */
|
||||||
|
|
@ -218,7 +218,7 @@ object Resolver
|
||||||
}
|
}
|
||||||
private def baseRepository[T](baseURI: java.net.URI)(construct: Patterns => T)(implicit basePatterns: Patterns): T =
|
private def baseRepository[T](baseURI: java.net.URI)(construct: Patterns => T)(implicit basePatterns: Patterns): T =
|
||||||
construct(resolvePatterns(baseURI.normalize, basePatterns))
|
construct(resolvePatterns(baseURI.normalize, basePatterns))
|
||||||
|
|
||||||
/** If `base` is None, `patterns` is returned unchanged.
|
/** If `base` is None, `patterns` is returned unchanged.
|
||||||
* Otherwise, the ivy file and artifact patterns in `patterns` are resolved against the given base. */
|
* Otherwise, the ivy file and artifact patterns in `patterns` are resolved against the given base. */
|
||||||
private def resolvePatterns(base: Option[String], patterns: Patterns): Patterns =
|
private def resolvePatterns(base: Option[String], patterns: Patterns): Patterns =
|
||||||
|
|
@ -236,7 +236,7 @@ object Resolver
|
||||||
}
|
}
|
||||||
/** Constructs a `URI` with the path component set to `path` and the other components set to null.*/
|
/** Constructs a `URI` with the path component set to `path` and the other components set to null.*/
|
||||||
private def pathURI(path: String) = new URI(null, null, path, null)
|
private def pathURI(path: String) = new URI(null, null, path, null)
|
||||||
|
|
||||||
def defaultFileConfiguration = FileConfiguration(true, None)
|
def defaultFileConfiguration = FileConfiguration(true, None)
|
||||||
def mavenStylePatterns = Patterns(Nil, mavenStyleBasePattern :: Nil, true)
|
def mavenStylePatterns = Patterns(Nil, mavenStyleBasePattern :: Nil, true)
|
||||||
def ivyStylePatterns = Patterns(Nil, Nil, false)
|
def ivyStylePatterns = Patterns(Nil, Nil, false)
|
||||||
|
|
@ -244,11 +244,11 @@ object Resolver
|
||||||
def defaultPatterns = mavenStylePatterns
|
def defaultPatterns = mavenStylePatterns
|
||||||
def mavenStyleBasePattern = "[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]"
|
def mavenStyleBasePattern = "[organisation]/[module]/[revision]/[artifact]-[revision](-[classifier]).[ext]"
|
||||||
def localBasePattern = "[organisation]/[module]/[revision]/[type]s/[artifact].[ext]"
|
def localBasePattern = "[organisation]/[module]/[revision]/[type]s/[artifact].[ext]"
|
||||||
|
|
||||||
def userRoot = System.getProperty("user.home")
|
def userRoot = System.getProperty("user.home")
|
||||||
def userMavenRoot = userRoot + "/.m2/repository/"
|
def userMavenRoot = userRoot + "/.m2/repository/"
|
||||||
def userIvyRoot = userRoot + "/.ivy2/"
|
def userIvyRoot = userRoot + "/.ivy2/"
|
||||||
|
|
||||||
def defaultLocal = defaultUserFileRepository("local")
|
def defaultLocal = defaultUserFileRepository("local")
|
||||||
def defaultShared = defaultUserFileRepository("shared")
|
def defaultShared = defaultUserFileRepository("shared")
|
||||||
def defaultUserFileRepository(id: String) = file(id, new File(userIvyRoot, id))(defaultIvyPatterns)
|
def defaultUserFileRepository(id: String) = file(id, new File(userIvyRoot, id))(defaultIvyPatterns)
|
||||||
|
|
@ -263,7 +263,7 @@ object Configurations
|
||||||
{
|
{
|
||||||
def config(name: String) = new Configuration(name)
|
def config(name: String) = new Configuration(name)
|
||||||
def defaultMavenConfigurations = Compile :: Runtime :: Test :: Provided :: System :: Optional :: Sources :: Javadoc :: Nil
|
def defaultMavenConfigurations = Compile :: Runtime :: Test :: Provided :: System :: Optional :: Sources :: Javadoc :: Nil
|
||||||
|
|
||||||
lazy val Default = config("default")
|
lazy val Default = config("default")
|
||||||
lazy val Compile = config("compile")
|
lazy val Compile = config("compile")
|
||||||
lazy val IntegrationTest = config("it") hide
|
lazy val IntegrationTest = config("it") hide
|
||||||
|
|
@ -276,7 +276,7 @@ object Configurations
|
||||||
lazy val Optional = config("optional")
|
lazy val Optional = config("optional")
|
||||||
|
|
||||||
lazy val CompilerPlugin = config("plugin") hide
|
lazy val CompilerPlugin = config("plugin") hide
|
||||||
|
|
||||||
private[xsbt] val DefaultMavenConfiguration = defaultConfiguration(true)
|
private[xsbt] val DefaultMavenConfiguration = defaultConfiguration(true)
|
||||||
private[xsbt] val DefaultIvyConfiguration = defaultConfiguration(false)
|
private[xsbt] val DefaultIvyConfiguration = defaultConfiguration(false)
|
||||||
private[xsbt] def DefaultConfiguration(mavenStyle: Boolean) = if(mavenStyle) DefaultMavenConfiguration else DefaultIvyConfiguration
|
private[xsbt] def DefaultConfiguration(mavenStyle: Boolean) = if(mavenStyle) DefaultMavenConfiguration else DefaultIvyConfiguration
|
||||||
|
|
@ -285,7 +285,7 @@ object Configurations
|
||||||
val base = if(mavenStyle) Configurations.Compile else Configurations.Default
|
val base = if(mavenStyle) Configurations.Compile else Configurations.Default
|
||||||
config(base.name + "->default(compile)")
|
config(base.name + "->default(compile)")
|
||||||
}
|
}
|
||||||
|
|
||||||
private[xsbt] def removeDuplicates(configs: Iterable[Configuration]) = Set(scala.collection.mutable.Map(configs.map(config => (config.name, config)).toSeq: _*).values.toList: _*)
|
private[xsbt] def removeDuplicates(configs: Iterable[Configuration]) = Set(scala.collection.mutable.Map(configs.map(config => (config.name, config)).toSeq: _*).values.toList: _*)
|
||||||
}
|
}
|
||||||
/** Represents an Ivy configuration. */
|
/** Represents an Ivy configuration. */
|
||||||
|
|
@ -311,8 +311,8 @@ object Artifact
|
||||||
def apply(name: String, url: URL): Artifact =Artifact(name, extract(url, defaultType), extract(url, defaultExtension), None, Nil, Some(url))
|
def apply(name: String, url: URL): Artifact =Artifact(name, extract(url, defaultType), extract(url, defaultExtension), None, Nil, Some(url))
|
||||||
val defaultExtension = "jar"
|
val defaultExtension = "jar"
|
||||||
val defaultType = "jar"
|
val defaultType = "jar"
|
||||||
private[this] def extract(url: URL, default: String): String = extract(url.toString, default)
|
def extract(url: URL, default: String): String = extract(url.toString, default)
|
||||||
private[this] def extract(name: String, default: String): String =
|
def extract(name: String, default: String): String =
|
||||||
{
|
{
|
||||||
val i = name.lastIndexOf('.')
|
val i = name.lastIndexOf('.')
|
||||||
if(i >= 0)
|
if(i >= 0)
|
||||||
|
|
@ -346,7 +346,7 @@ object Credentials
|
||||||
{
|
{
|
||||||
val properties = new scala.collection.mutable.HashMap[String, String]
|
val properties = new scala.collection.mutable.HashMap[String, String]
|
||||||
def get(keys: List[String]) = keys.flatMap(properties.get).firstOption.toRight(keys.head + " not specified in credentials file: " + path)
|
def get(keys: List[String]) = keys.flatMap(properties.get).firstOption.toRight(keys.head + " not specified in credentials file: " + path)
|
||||||
|
|
||||||
impl.MapUtilities.read(properties, path, log) orElse
|
impl.MapUtilities.read(properties, path, log) orElse
|
||||||
{
|
{
|
||||||
List.separate( List(RealmKeys, HostKeys, UserKeys, PasswordKeys).map(get) ) match
|
List.separate( List(RealmKeys, HostKeys, UserKeys, PasswordKeys).map(get) ) match
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,95 @@
|
||||||
|
package xsbt
|
||||||
|
|
||||||
|
import java.io.File
|
||||||
|
import org.specs._
|
||||||
|
import FileUtilities.{createDirectory, delete, touch, withTemporaryDirectory}
|
||||||
|
import org.apache.ivy.util.ChecksumHelper
|
||||||
|
|
||||||
|
object ComponentManagerTest extends Specification
|
||||||
|
{
|
||||||
|
val TestID = "manager-test"
|
||||||
|
"Component manager" should {
|
||||||
|
"throw an exception if 'file' is called for a non-existing component" in {
|
||||||
|
withManager { _.file(TestID) must throwA[InvalidComponent] }
|
||||||
|
}
|
||||||
|
"throw an exception if 'file' is called for an empty component" in {
|
||||||
|
withManager { manager =>
|
||||||
|
createDirectory(manager.location(TestID))
|
||||||
|
( manager.file(TestID) ) must throwA[InvalidComponent]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"return the file for a single-file component" in {
|
||||||
|
withManager { manager =>
|
||||||
|
createFiles(manager, TestID, "a") match { case Seq(x) =>
|
||||||
|
manager.file(TestID).getAbsoluteFile must beEqualTo(x.getAbsoluteFile)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"throw an exception if 'file' is called for multi-file component" in {
|
||||||
|
withManager { manager =>
|
||||||
|
createFiles(manager, TestID, "a", "b")
|
||||||
|
( manager.file(TestID) ) must throwA[InvalidComponent]
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"return the files for a multi-file component" in {
|
||||||
|
withManager { manager =>
|
||||||
|
val files = createFiles(manager, TestID, "a", "b")
|
||||||
|
manager.files(TestID) must haveTheSameElementsAs(files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"return the files for a single-file component" in {
|
||||||
|
withManager { manager =>
|
||||||
|
val files = createFiles(manager, TestID, "a")
|
||||||
|
manager.files(TestID) must haveTheSameElementsAs(files)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
"throw an exception if 'files' is called for a non-existing component" in {
|
||||||
|
withManager { _.files(TestID) must throwA[InvalidComponent] }
|
||||||
|
}
|
||||||
|
|
||||||
|
"properly cache a file and then retrieve it to an unresolved component" in {
|
||||||
|
withManager { manager =>
|
||||||
|
val file = createFile(manager, TestID, "a")
|
||||||
|
val hash = checksum(file)
|
||||||
|
try
|
||||||
|
{
|
||||||
|
manager.cache(TestID)
|
||||||
|
delete(manager.location(TestID))
|
||||||
|
FileUtilities.listFiles(manager.location(TestID)).toList must haveSize(0)
|
||||||
|
checksum(manager.file(TestID)) must beEqualTo(hash)
|
||||||
|
}
|
||||||
|
finally { manager.clearCache(TestID) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
"not retrieve to a component already resolved" in {
|
||||||
|
withManager { manager =>
|
||||||
|
val file = createFile(manager, TestID, "a")
|
||||||
|
try
|
||||||
|
{
|
||||||
|
manager.cache(TestID)
|
||||||
|
val idDirectory = manager.location(TestID)
|
||||||
|
delete(idDirectory)
|
||||||
|
createDirectory(idDirectory)
|
||||||
|
manager.file(TestID) must throwA[InvalidComponent]
|
||||||
|
}
|
||||||
|
finally { manager.clearCache(TestID) }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private def checksum(file: File) = ChecksumHelper.computeAsString(file, "sha1")
|
||||||
|
private def createFile(manager: ComponentManager, id: String, name: String): File = createFiles(manager, id, name).toList.head
|
||||||
|
private def createFiles(manager: ComponentManager, id: String, names: String*): Seq[File] =
|
||||||
|
{
|
||||||
|
val dir = manager.location(id)
|
||||||
|
createDirectory(dir)
|
||||||
|
names.map { name =>
|
||||||
|
val testFile = new File(dir, name)
|
||||||
|
touch(testFile)
|
||||||
|
testFile
|
||||||
|
}
|
||||||
|
}
|
||||||
|
private def withManager[T](f: ComponentManager => T): T =
|
||||||
|
TestIvyLogger( logger => withTemporaryDirectory { temp => f(new ComponentManager(temp, logger)) } )
|
||||||
|
}
|
||||||
|
|
@ -0,0 +1,9 @@
|
||||||
|
package xsbt
|
||||||
|
|
||||||
|
import xsbti.TestLogger
|
||||||
|
|
||||||
|
class TestIvyLogger extends TestLogger with IvyLogger
|
||||||
|
object TestIvyLogger
|
||||||
|
{
|
||||||
|
def apply[T](f: TestIvyLogger => T): T = TestLogger(new TestIvyLogger)(f)
|
||||||
|
}
|
||||||
|
|
@ -42,6 +42,9 @@ class XSbt(info: ProjectInfo) extends ParentProject(info)
|
||||||
class IvyProject(info: ProjectInfo) extends Base(info)
|
class IvyProject(info: ProjectInfo) extends Base(info)
|
||||||
{
|
{
|
||||||
val ivy = "org.apache.ivy" % "ivy" % "2.0.0"
|
val ivy = "org.apache.ivy" % "ivy" % "2.0.0"
|
||||||
|
// use IO from tests
|
||||||
|
override def testCompileAction = super.testCompileAction dependsOn(ioSub.testCompile)
|
||||||
|
override def testClasspath = super.testClasspath +++ ioSub.testClasspath
|
||||||
}
|
}
|
||||||
class InterfaceProject(info: ProjectInfo) extends DefaultProject(info)
|
class InterfaceProject(info: ProjectInfo) extends DefaultProject(info)
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -24,7 +24,7 @@ object FileUtilities
|
||||||
/** The size of the byte or char buffer used in various methods.*/
|
/** The size of the byte or char buffer used in various methods.*/
|
||||||
private val BufferSize = 8192
|
private val BufferSize = 8192
|
||||||
private val Newline = System.getProperty("line.separator")
|
private val Newline = System.getProperty("line.separator")
|
||||||
|
|
||||||
def classLocation(cl: Class[_]): URL =
|
def classLocation(cl: Class[_]): URL =
|
||||||
{
|
{
|
||||||
val codeSource = cl.getProtectionDomain.getCodeSource
|
val codeSource = cl.getProtectionDomain.getCodeSource
|
||||||
|
|
@ -34,12 +34,12 @@ object FileUtilities
|
||||||
def classLocationFile(cl: Class[_]): File = toFile(classLocation(cl))
|
def classLocationFile(cl: Class[_]): File = toFile(classLocation(cl))
|
||||||
def classLocation[T](implicit mf: SManifest[T]): URL = classLocation(mf.erasure)
|
def classLocation[T](implicit mf: SManifest[T]): URL = classLocation(mf.erasure)
|
||||||
def classLocationFile[T](implicit mf: SManifest[T]): File = classLocationFile(mf.erasure)
|
def classLocationFile[T](implicit mf: SManifest[T]): File = classLocationFile(mf.erasure)
|
||||||
|
|
||||||
def toFile(url: URL) =
|
def toFile(url: URL) =
|
||||||
try { new File(url.toURI) }
|
try { new File(url.toURI) }
|
||||||
catch { case _: URISyntaxException => new File(url.getPath) }
|
catch { case _: URISyntaxException => new File(url.getPath) }
|
||||||
|
|
||||||
|
|
||||||
// "base.extension" -> (base, extension)
|
// "base.extension" -> (base, extension)
|
||||||
def split(name: String): (String, String) =
|
def split(name: String): (String, String) =
|
||||||
{
|
{
|
||||||
|
|
@ -49,18 +49,29 @@ object FileUtilities
|
||||||
else
|
else
|
||||||
(name, "")
|
(name, "")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/** Creates a file at the given location.*/
|
||||||
|
def touch(file: File)
|
||||||
|
{
|
||||||
|
createDirectory(file.getParentFile)
|
||||||
|
val created = translate("Could not create file " + file) { file.createNewFile() }
|
||||||
|
if(created)
|
||||||
|
()
|
||||||
|
else if(file.isDirectory)
|
||||||
|
error("File exists and is a directory.")
|
||||||
|
else if(!file.setLastModified(System.currentTimeMillis))
|
||||||
|
error("Could not update last modified time for file " + file)
|
||||||
|
}
|
||||||
def createDirectory(dir: File): Unit =
|
def createDirectory(dir: File): Unit =
|
||||||
translate("Could not create directory " + dir + ": ")
|
{
|
||||||
{
|
def failBase = "Could not create directory " + dir
|
||||||
if(dir.exists)
|
if(dir.isDirectory || dir.mkdirs())
|
||||||
{
|
()
|
||||||
if(!dir.isDirectory)
|
else if(dir.exists)
|
||||||
error("file exists and is not a directory.")
|
error(failBase + ": file exists and is not a directory.")
|
||||||
}
|
else
|
||||||
else if(!dir.mkdirs())
|
error(failBase)
|
||||||
error("<unknown error>")
|
}
|
||||||
}
|
|
||||||
def unzip(from: File, toDirectory: File): Set[File] = unzip(from, toDirectory, AllPassFilter)
|
def unzip(from: File, toDirectory: File): Set[File] = unzip(from, toDirectory, AllPassFilter)
|
||||||
def unzip(from: File, toDirectory: File, filter: NameFilter): Set[File] = fileInputStream(from)(in => unzip(in, toDirectory, filter))
|
def unzip(from: File, toDirectory: File, filter: NameFilter): Set[File] = fileInputStream(from)(in => unzip(in, toDirectory, filter))
|
||||||
def unzip(from: InputStream, toDirectory: File, filter: NameFilter): Set[File] =
|
def unzip(from: InputStream, toDirectory: File, filter: NameFilter): Set[File] =
|
||||||
|
|
@ -105,7 +116,7 @@ object FileUtilities
|
||||||
next()
|
next()
|
||||||
Set() ++ set
|
Set() ++ set
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Copies all bytes from the given input stream to the given output stream.
|
/** Copies all bytes from the given input stream to the given output stream.
|
||||||
* Neither stream is closed.*/
|
* Neither stream is closed.*/
|
||||||
def transfer(in: InputStream, out: OutputStream): Unit = transferImpl(in, out, false)
|
def transfer(in: InputStream, out: OutputStream): Unit = transferImpl(in, out, false)
|
||||||
|
|
@ -130,7 +141,7 @@ object FileUtilities
|
||||||
}
|
}
|
||||||
finally { if(close) in.close }
|
finally { if(close) in.close }
|
||||||
}
|
}
|
||||||
|
|
||||||
/** Creates a temporary directory and provides its location to the given function. The directory
|
/** Creates a temporary directory and provides its location to the given function. The directory
|
||||||
* is deleted after the function returns.*/
|
* is deleted after the function returns.*/
|
||||||
def withTemporaryDirectory[T](action: File => T): T =
|
def withTemporaryDirectory[T](action: File => T): T =
|
||||||
|
|
@ -149,16 +160,16 @@ object FileUtilities
|
||||||
{
|
{
|
||||||
val randomName = "sbt_" + java.lang.Integer.toHexString(random.nextInt)
|
val randomName = "sbt_" + java.lang.Integer.toHexString(random.nextInt)
|
||||||
val f = new File(temporaryDirectory, randomName)
|
val f = new File(temporaryDirectory, randomName)
|
||||||
|
|
||||||
try { createDirectory(f); f }
|
try { createDirectory(f); f }
|
||||||
catch { case e: Exception => create(tries + 1) }
|
catch { case e: Exception => create(tries + 1) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
create(0)
|
create(0)
|
||||||
}
|
}
|
||||||
|
|
||||||
private[xsbt] def jars(dir: File): Iterable[File] = wrapNull(dir.listFiles(GlobFilter("*.jar")))
|
private[xsbt] def jars(dir: File): Iterable[File] = listFiles(dir, GlobFilter("*.jar"))
|
||||||
|
|
||||||
def delete(files: Iterable[File]): Unit = files.foreach(delete)
|
def delete(files: Iterable[File]): Unit = files.foreach(delete)
|
||||||
def delete(file: File)
|
def delete(file: File)
|
||||||
{
|
{
|
||||||
|
|
@ -166,20 +177,24 @@ object FileUtilities
|
||||||
{
|
{
|
||||||
if(file.isDirectory)
|
if(file.isDirectory)
|
||||||
{
|
{
|
||||||
delete(wrapNull(file.listFiles))
|
delete(listFiles(file))
|
||||||
file.delete
|
file.delete
|
||||||
}
|
}
|
||||||
else if(file.exists)
|
else if(file.exists)
|
||||||
file.delete
|
file.delete
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
private def wrapNull(a: Array[File]): Array[File] =
|
def listFiles(dir: File, filter: FileFilter): Array[File] = wrapNull(dir.listFiles(filter))
|
||||||
|
def listFiles(dir: File): Array[File] = wrapNull(dir.listFiles())
|
||||||
|
private def wrapNull(a: Array[File]) =
|
||||||
|
{
|
||||||
if(a == null)
|
if(a == null)
|
||||||
new Array[File](0)
|
new Array[File](0)
|
||||||
else
|
else
|
||||||
a
|
a
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
/** Creates a jar file.
|
/** Creates a jar file.
|
||||||
* @param sources The files to include in the jar file.
|
* @param sources The files to include in the jar file.
|
||||||
* @param outputJar The file to write the jar to.
|
* @param outputJar The file to write the jar to.
|
||||||
|
|
@ -196,7 +211,7 @@ object FileUtilities
|
||||||
* @param mapper The mapper that determines the name of a File in the jar. */
|
* @param mapper The mapper that determines the name of a File in the jar. */
|
||||||
def zip(sources: Iterable[File], outputZip: File, recursive: Boolean, mapper: PathMapper): Unit =
|
def zip(sources: Iterable[File], outputZip: File, recursive: Boolean, mapper: PathMapper): Unit =
|
||||||
archive(sources, outputZip, None, recursive, mapper)
|
archive(sources, outputZip, None, recursive, mapper)
|
||||||
|
|
||||||
private def archive(sources: Iterable[File], outputFile: File, manifest: Option[Manifest], recursive: Boolean, mapper: PathMapper)
|
private def archive(sources: Iterable[File], outputFile: File, manifest: Option[Manifest], recursive: Boolean, mapper: PathMapper)
|
||||||
{
|
{
|
||||||
if(outputFile.isDirectory)
|
if(outputFile.isDirectory)
|
||||||
|
|
@ -219,7 +234,7 @@ object FileUtilities
|
||||||
if(sourceFile.isDirectory)
|
if(sourceFile.isDirectory)
|
||||||
{
|
{
|
||||||
if(recursive)
|
if(recursive)
|
||||||
wrapNull(sourceFile.listFiles).foreach(add)
|
listFiles(sourceFile).foreach(add)
|
||||||
}
|
}
|
||||||
else if(sourceFile.exists)
|
else if(sourceFile.exists)
|
||||||
{
|
{
|
||||||
|
|
@ -235,7 +250,7 @@ object FileUtilities
|
||||||
sources.foreach(add)
|
sources.foreach(add)
|
||||||
output.closeEntry()
|
output.closeEntry()
|
||||||
}
|
}
|
||||||
|
|
||||||
private def withZipOutput(file: File, manifest: Option[Manifest])(f: ZipOutputStream => Unit)
|
private def withZipOutput(file: File, manifest: Option[Manifest])(f: ZipOutputStream => Unit)
|
||||||
{
|
{
|
||||||
fileOutputStream(false)(file) { fileOut =>
|
fileOutputStream(false)(file) { fileOut =>
|
||||||
|
|
@ -332,5 +347,5 @@ object FileUtilities
|
||||||
else
|
else
|
||||||
error("String cannot be encoded by charset " + charset.name)
|
error("String cannot be encoded by charset " + charset.name)
|
||||||
}
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue