Turned sbt launcher into a general Scala application launcher as described in launch.specification

This commit is contained in:
Mark Harrah 2009-09-27 14:39:26 -04:00
parent b6b7fe2f9b
commit abd06a17c5
9 changed files with 108 additions and 113 deletions

View File

@ -1,7 +1,6 @@
package xsbt package xsbt
import java.io.File import java.io.File
import xsbti.Versions
/** A component manager provides access to the pieces of xsbt that are distributed as components. /** 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. * There are two types of components. The first type is compiled subproject jars with their dependencies.
@ -12,30 +11,13 @@ import xsbti.Versions
* This is used for compiled 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(provider: xsbti.ComponentProvider, 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)
/** 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 =
{
val dir = location(id)
if(!dir.exists)
update(id)
dir
}
// get the contents of the given directory, wrapping a null result in an empty list.
private def contents(dir: File): Seq[File] =
{
val fs = dir.listFiles
if(fs == null) Nil else fs
}
/** Get all of the files for component 'id', throwing an exception if no files exist for the component. */ /** 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 existing = provider.component(id)
val fs = if(existing.isEmpty) { update(id); provider.component(id) } else existing
if(!fs.isEmpty) fs else invalid("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. */ /** Get the file for component 'id', throwing an exception if no files or multiple files exist for the component. */
@ -47,12 +29,13 @@ class ComponentManager(baseDirectory: File, log: IvyLogger) extends NotNull
private def invalid(msg: String) = throw new InvalidComponent(msg) private def invalid(msg: String) = throw new InvalidComponent(msg)
private def invalid(e: NotInCache) = throw new InvalidComponent(e.getMessage, e) private def invalid(e: NotInCache) = throw new InvalidComponent(e.getMessage, e)
def define(id: String, files: Iterable[File]) = provider.defineComponent(id, files.toSeq.toArray)
/** Retrieve the file for component 'id' from the local repository. */ /** Retrieve the file for component 'id' from the local repository. */
def update(id: String): Unit = def update(id: String): Unit =
try { IvyCache.retrieveCachedJar(sbtModuleID(id), location(id), log) } try { IvyCache.withCachedJar(sbtModuleID(id), log)(jar => define(id, Seq(jar)) ) }
catch { case e: NotInCache => invalid(e) } 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, xsbti.Versions.Sbt)
/** Install the files for component 'id' to the local repository. This is usually used after writing files to the directory returned by 'location'. */ /** 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 cache(id: String): Unit = IvyCache.cacheJar(sbtModuleID(id), file(id), log)
def clearCache(id: String): Unit = IvyCache.clearCachedJar(sbtModuleID(id), log) def clearCache(id: String): Unit = IvyCache.clearCachedJar(sbtModuleID(id), log)

View File

@ -91,7 +91,7 @@ private object ConvertResolver
setKeyFilePassword(password) setKeyFilePassword(password)
} }
} }
private def initializePatterns(resolver: AbstractPatternsBasedResolver, patterns: RepositoryHelpers.Patterns) private def initializePatterns(resolver: AbstractPatternsBasedResolver, patterns: Patterns)
{ {
resolver.setM2compatible(patterns.isMavenCompatible) resolver.setM2compatible(patterns.isMavenCompatible)
patterns.ivyPatterns.foreach(resolver.addIvyPattern) patterns.ivyPatterns.foreach(resolver.addIvyPattern)

View File

@ -17,8 +17,10 @@ import plugins.repository.url.URLResource
private[xsbt] object CustomXmlParser extends XmlModuleDescriptorParser with NotNull private[xsbt] object CustomXmlParser extends XmlModuleDescriptorParser with NotNull
{ {
import XmlModuleDescriptorParser.Parser import XmlModuleDescriptorParser.Parser
class CustomParser(settings: IvySettings) extends Parser(CustomXmlParser, settings) with NotNull class CustomParser(settings: IvySettings, defaultConfig: Option[String]) extends Parser(CustomXmlParser, settings) with NotNull
{ {
defaultConfig.foreach(x => setDefaultConfMapping("*->default(compile)"))
def setSource(url: URL) = def setSource(url: URL) =
{ {
super.setResource(new URLResource(url)) super.setResource(new URLResource(url))
@ -29,7 +31,6 @@ private[xsbt] object CustomXmlParser extends XmlModuleDescriptorParser with NotN
override def setResource(res: Resource) {} override def setResource(res: Resource) {}
override def setMd(md: DefaultModuleDescriptor) = super.setMd(md) override def setMd(md: DefaultModuleDescriptor) = super.setMd(md)
override def parseDepsConfs(confs: String, dd: DefaultDependencyDescriptor) = super.parseDepsConfs(confs, dd) override def parseDepsConfs(confs: String, dd: DefaultDependencyDescriptor) = super.parseDepsConfs(confs, dd)
override def getDefaultConf = super.getDefaultConf override def getDefaultConf = defaultConfig.getOrElse(super.getDefaultConf)
override def setDefaultConf(conf: String) = super.setDefaultConf(conf)
} }
} }

View File

@ -8,6 +8,7 @@ import Artifact.{defaultExtension, defaultType}
import java.io.File import java.io.File
import org.apache.ivy.{core, plugins, util, Ivy} import org.apache.ivy.{core, plugins, util, Ivy}
import core.IvyPatternHelper
import core.cache.DefaultRepositoryCacheManager import core.cache.DefaultRepositoryCacheManager
import core.module.descriptor.{DefaultArtifact, DefaultDependencyArtifactDescriptor, MDArtifact} import core.module.descriptor.{DefaultArtifact, DefaultDependencyArtifactDescriptor, MDArtifact}
import core.module.descriptor.{DefaultDependencyDescriptor, DefaultModuleDescriptor, ModuleDescriptor} import core.module.descriptor.{DefaultDependencyDescriptor, DefaultModuleDescriptor, ModuleDescriptor}
@ -74,7 +75,7 @@ final class IvySbt(configuration: IvyConfiguration)
finally { ivy.popContext() } finally { ivy.popContext() }
} }
final class Module(val moduleConfiguration: ModuleConfiguration) extends NotNull final class Module(val moduleSettings: ModuleSettings) 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 =
@ -83,14 +84,14 @@ final class IvySbt(configuration: IvyConfiguration)
private lazy val (moduleDescriptor: DefaultModuleDescriptor, defaultConfig: String) = private lazy val (moduleDescriptor: DefaultModuleDescriptor, defaultConfig: String) =
{ {
val (baseModule, baseConfiguration) = val (baseModule, baseConfiguration) =
moduleConfiguration match moduleSettings match
{ {
case ic: InlineConfiguration => configureInline(ic) case ic: InlineConfiguration => configureInline(ic)
case ec: EmptyConfiguration => configureEmpty(ec.module) case ec: EmptyConfiguration => configureEmpty(ec.module)
case pc: PomConfiguration => readPom(pc.file, pc.validate) case pc: PomConfiguration => readPom(pc.file, pc.validate)
case ifc: IvyFileConfiguration => readIvyFile(ifc.file, ifc.validate) case ifc: IvyFileConfiguration => readIvyFile(ifc.file, ifc.validate)
} }
moduleConfiguration.ivyScala.foreach(IvyScala.checkModule(baseModule, baseConfiguration)) moduleSettings.ivyScala.foreach(IvyScala.checkModule(baseModule, baseConfiguration))
baseModule.getExtraAttributesNamespaces.asInstanceOf[java.util.Map[String,String]].put("e", "http://ant.apache.org/ivy/extra") baseModule.getExtraAttributesNamespaces.asInstanceOf[java.util.Map[String,String]].put("e", "http://ant.apache.org/ivy/extra")
(baseModule, baseConfiguration) (baseModule, baseConfiguration)
} }
@ -105,6 +106,7 @@ final class IvySbt(configuration: IvyConfiguration)
IvySbt.addArtifacts(moduleID, artifacts) IvySbt.addArtifacts(moduleID, artifacts)
IvySbt.addDependencies(moduleID, dependencies, parser) IvySbt.addDependencies(moduleID, dependencies, parser)
IvySbt.setModuleConfigurations(settings, moduleConfigurations)
IvySbt.addMainArtifact(moduleID) IvySbt.addMainArtifact(moduleID)
(moduleID, parser.getDefaultConf) (moduleID, parser.getDefaultConf)
} }
@ -126,7 +128,7 @@ final class IvySbt(configuration: IvyConfiguration)
private def readIvyFile(ivyFile: File, validate: Boolean) = private def readIvyFile(ivyFile: File, validate: Boolean) =
{ {
val url = toURL(ivyFile) val url = toURL(ivyFile)
val parser = new CustomXmlParser.CustomParser(settings) val parser = new CustomXmlParser.CustomParser(settings, None)
parser.setValidate(validate) parser.setValidate(validate)
parser.setSource(url) parser.setSource(url)
parser.parse() parser.parse()
@ -168,6 +170,20 @@ private object IvySbt
settings.setDefaultResolver(newDefault.getName) settings.setDefaultResolver(newDefault.getName)
log.debug("Using repositories:\n" + resolvers.mkString("\n\t")) log.debug("Using repositories:\n" + resolvers.mkString("\n\t"))
} }
private def setModuleConfigurations(settings: IvySettings, moduleConfigurations: Seq[ModuleConfiguration])
{
val existing = settings.getResolverNames
for(moduleConf <- moduleConfigurations)
{
import moduleConf._
import IvyPatternHelper._
import PatternMatcher._
if(!existing.contains(resolver.name))
settings.addResolver(ConvertResolver(resolver))
val attributes = javaMap(Map(MODULE_KEY -> name, ORGANISATION_KEY -> organization, REVISION_KEY -> revision))
settings.addModuleConfiguration(attributes, settings.getMatcher(EXACT_OR_REGEXP), resolver.name, null, null, null)
}
}
private def configureCache(settings: IvySettings, dir: Option[File]) private def configureCache(settings: IvySettings, dir: Option[File])
{ {
val cacheDir = dir.getOrElse(settings.getDefaultRepositoryCacheBasedir()) val cacheDir = dir.getOrElse(settings.getDefaultRepositoryCacheBasedir())
@ -244,9 +260,8 @@ private object IvySbt
/** Parses the given in-memory Ivy file 'xml', using the existing 'moduleID' and specifying the given 'defaultConfiguration'. */ /** Parses the given in-memory Ivy file 'xml', using the existing 'moduleID' and specifying the given 'defaultConfiguration'. */
private def parseIvyXML(settings: IvySettings, xml: String, moduleID: DefaultModuleDescriptor, defaultConfiguration: String, validate: Boolean): CustomXmlParser.CustomParser = private def parseIvyXML(settings: IvySettings, xml: String, moduleID: DefaultModuleDescriptor, defaultConfiguration: String, validate: Boolean): CustomXmlParser.CustomParser =
{ {
val parser = new CustomXmlParser.CustomParser(settings) val parser = new CustomXmlParser.CustomParser(settings, Some(defaultConfiguration))
parser.setMd(moduleID) parser.setMd(moduleID)
parser.setDefaultConf(defaultConfiguration)
parser.setValidate(validate) parser.setValidate(validate)
parser.setInput(xml.getBytes) parser.setInput(xml.getBytes)
parser.parse() parser.parse()

View File

@ -27,7 +27,7 @@ object IvyActions
{ {
module.logger.info("Installing " + dependency) module.logger.info("Installing " + dependency)
val options = new InstallOptions val options = new InstallOptions
options.setValidate(module.moduleConfiguration.validate) options.setValidate(module.moduleSettings.validate)
options.setTransitive(dependency.isTransitive) options.setTransitive(dependency.isTransitive)
ivy.install(dependency.getDependencyRevisionId, from, to, options) ivy.install(dependency.getDependencyRevisionId, from, to, options)
} }
@ -50,10 +50,8 @@ object IvyActions
// todo: correct default configuration for extra dependencies // todo: correct default configuration for extra dependencies
private def addLateDependencies(ivy: Ivy, module: DefaultModuleDescriptor, defaultConfiguration: String, extraDependencies: Iterable[ModuleID]) private def addLateDependencies(ivy: Ivy, module: DefaultModuleDescriptor, defaultConfiguration: String, extraDependencies: Iterable[ModuleID])
{ {
val parser = new CustomXmlParser.CustomParser(ivy.getSettings) val parser = new CustomXmlParser.CustomParser(ivy.getSettings, Some(defaultConfiguration))
parser.setMd(module) parser.setMd(module)
val defaultConf = if(defaultConfiguration.contains("->")) defaultConfiguration else (defaultConfiguration + "->default(compile)")
parser.setDefaultConf(defaultConf)
IvySbt.addDependencies(module, extraDependencies, parser) IvySbt.addDependencies(module, extraDependencies, parser)
} }
private def getConfigurations(module: ModuleDescriptor, configurations: Option[Iterable[Configuration]]) = private def getConfigurations(module: ModuleDescriptor, configurations: Option[Iterable[Configuration]]) =

View File

@ -40,19 +40,20 @@ object IvyCache
/** Clears the cache of the jar for the given ID.*/ /** Clears the cache of the jar for the given ID.*/
def clearCachedJar(id: ModuleID, log: IvyLogger) def clearCachedJar(id: ModuleID, log: IvyLogger)
{ {
try { getCachedFile(id, log).delete } try { withCachedJar(id, log)(_.delete) }
catch { case e: Exception => log.debug("Error cleaning cached jar: " + e.toString) } 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.*/ /** 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) = def retrieveCachedJar(id: ModuleID, toDirectory: File, log: IvyLogger) =
{ withCachedJar(id, log) { cachedFile =>
val cachedFile = getCachedFile(id, log) val copyTo = new File(toDirectory, cachedFile.getName)
val copyTo = new File(toDirectory, cachedFile.getName) FileUtil.copy(cachedFile, copyTo, null)
FileUtil.copy(cachedFile, copyTo, null) copyTo
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.*/ /** 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 = * TODO: locking.*/
def withCachedJar[T](id: ModuleID, log: IvyLogger)(f: File => T): T =
{ {
val cachedFile = val cachedFile =
try try
@ -64,7 +65,7 @@ object IvyCache
} }
catch { case e: Exception => throw new NotInCache(id, e) } catch { case e: Exception => throw new NotInCache(id, e) }
if(cachedFile.exists) cachedFile else throw new NotInCache(id) if(cachedFile.exists) f(cachedFile) else throw new NotInCache(id)
} }
/** Calls the given function with the default Ivy cache.*/ /** Calls the given function with the default Ivy cache.*/
def withDefaultCache[T](log: IvyLogger)(f: DefaultRepositoryCacheManager => T): T = def withDefaultCache[T](log: IvyLogger)(f: DefaultRepositoryCacheManager => T): T =
@ -82,7 +83,7 @@ object IvyCache
{ {
val local = Resolver.defaultLocal val local = Resolver.defaultLocal
val paths = new IvyPaths(new File("."), None) val paths = new IvyPaths(new File("."), None)
val conf = new IvyConfiguration(paths, Seq(local), log) val conf = new IvyConfiguration(paths, Seq(local), Nil, log)
(new IvySbt(conf), local) (new IvySbt(conf), local)
} }
/** Creates a default jar artifact based on the given ID.*/ /** Creates a default jar artifact based on the given ID.*/

View File

@ -7,19 +7,20 @@ import java.io.File
import scala.xml.NodeSeq import scala.xml.NodeSeq
final class IvyPaths(val baseDirectory: File, val cacheDirectory: Option[File]) extends NotNull final class IvyPaths(val baseDirectory: File, val cacheDirectory: Option[File]) extends NotNull
final class IvyConfiguration(val paths: IvyPaths, val resolvers: Seq[Resolver], val log: IvyLogger) extends NotNull final class IvyConfiguration(val paths: IvyPaths, val resolvers: Seq[Resolver],
val moduleConfigurations: Seq[ModuleConfiguration], val log: IvyLogger) extends NotNull
sealed trait ModuleConfiguration extends NotNull sealed trait ModuleSettings extends NotNull
{ {
def validate: Boolean def validate: Boolean
def ivyScala: Option[IvyScala] def ivyScala: Option[IvyScala]
} }
final class IvyFileConfiguration(val file: File, val ivyScala: Option[IvyScala], val validate: Boolean) extends ModuleConfiguration final class IvyFileConfiguration(val file: File, val ivyScala: Option[IvyScala], val validate: Boolean) extends ModuleSettings
final class PomConfiguration(val file: File, val ivyScala: Option[IvyScala], val validate: Boolean) extends ModuleConfiguration final class PomConfiguration(val file: File, val ivyScala: Option[IvyScala], val validate: Boolean) extends ModuleSettings
final class InlineConfiguration(val module: ModuleID, val dependencies: Iterable[ModuleID], val ivyXML: NodeSeq, final class InlineConfiguration(val module: ModuleID, val dependencies: Iterable[ModuleID], val ivyXML: NodeSeq,
val configurations: Iterable[Configuration], val defaultConfiguration: Option[Configuration], val ivyScala: Option[IvyScala], val configurations: Iterable[Configuration], val defaultConfiguration: Option[Configuration], val ivyScala: Option[IvyScala],
val artifacts: Iterable[Artifact], val validate: Boolean) extends ModuleConfiguration val artifacts: Iterable[Artifact], val validate: Boolean) extends ModuleSettings
final class EmptyConfiguration(val module: ModuleID, val ivyScala: Option[IvyScala], val validate: Boolean) extends ModuleConfiguration final class EmptyConfiguration(val module: ModuleID, val ivyScala: Option[IvyScala], val validate: Boolean) extends ModuleSettings
object InlineConfiguration object InlineConfiguration
{ {
def apply(module: ModuleID, dependencies: Iterable[ModuleID], artifacts: Iterable[Artifact]) = def apply(module: ModuleID, dependencies: Iterable[ModuleID], artifacts: Iterable[Artifact]) =
@ -37,7 +38,7 @@ object InlineConfiguration
else else
explicitConfigurations explicitConfigurations
} }
object ModuleConfiguration object ModuleSettings
{ {
def apply(ivyScala: Option[IvyScala], validate: Boolean, module: => ModuleID)(baseDirectory: File, log: IvyLogger) = def apply(ivyScala: Option[IvyScala], validate: Boolean, module: => ModuleID)(baseDirectory: File, log: IvyLogger) =
{ {

View File

@ -19,7 +19,7 @@ final case class ModuleID(organization: String, name: String, revision: String,
def from(url: String) = artifacts(Artifact(name, new URL(url))) def from(url: String) = artifacts(Artifact(name, new URL(url)))
def classifier(c: String) = artifacts(Artifact(name, c)) def classifier(c: String) = artifacts(Artifact(name, c))
def artifacts(newArtifacts: Artifact*) = ModuleID(organization, name, revision, configurations, isChanging, isTransitive, newArtifacts ++ explicitArtifacts, extraAttributes) def artifacts(newArtifacts: Artifact*) = ModuleID(organization, name, revision, configurations, isChanging, isTransitive, newArtifacts ++ explicitArtifacts, extraAttributes)
def extra(attributes: (String,String)*) = ModuleID(organization, name, revision, configurations, isChanging, isTransitive, explicitArtifacts, extraAttributes ++ attributes) def extra(attributes: (String,String)*) = ModuleID(organization, name, revision, configurations, isChanging, isTransitive, explicitArtifacts, extraAttributes ++ ModuleID.checkE(attributes))
} }
object ModuleID object ModuleID
{ {
@ -30,6 +30,10 @@ object ModuleID
ModuleID(organization, name, revision, configurations, isChanging, isTransitive, Nil) ModuleID(organization, name, revision, configurations, isChanging, isTransitive, Nil)
def apply(organization: String, name: String, revision: String, configurations: Option[String], isChanging: Boolean, isTransitive: Boolean, explicitArtifacts: Seq[Artifact]): ModuleID = def apply(organization: String, name: String, revision: String, configurations: Option[String], isChanging: Boolean, isTransitive: Boolean, explicitArtifacts: Seq[Artifact]): ModuleID =
ModuleID(organization, name, revision, configurations, isChanging, isTransitive, explicitArtifacts, Map.empty) ModuleID(organization, name, revision, configurations, isChanging, isTransitive, explicitArtifacts, Map.empty)
def checkE(attributes: Seq[(String, String)]) =
for ( (key, value) <- attributes) yield
if(key.startsWith("e:")) (key, value) else ("e:" + key, value)
} }
sealed trait Resolver extends NotNull sealed trait Resolver extends NotNull
{ {
@ -40,14 +44,20 @@ sealed case class MavenRepository(name: String, root: String) extends Resolver
override def toString = name + ": " + root override def toString = name + ": " + root
} }
final class Patterns(val ivyPatterns: Seq[String], val artifactPatterns: Seq[String], val isMavenCompatible: Boolean) extends NotNull
{
private[xsbt] def mavenStyle(): Patterns = Patterns(ivyPatterns, artifactPatterns, true)
private[xsbt] def withIvys(patterns: Seq[String]): Patterns = Patterns(patterns ++ ivyPatterns, artifactPatterns, isMavenCompatible)
private[xsbt] def withArtifacts(patterns: Seq[String]): Patterns = Patterns(ivyPatterns, patterns ++ artifactPatterns, isMavenCompatible)
}
object Patterns
{
def apply(artifactPatterns: String*): Patterns = Patterns(true, artifactPatterns : _*)
def apply(isMavenCompatible: Boolean, artifactPatterns: String*): Patterns = Patterns(Nil, artifactPatterns, isMavenCompatible)
def apply(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean): Patterns = new Patterns(ivyPatterns, artifactPatterns, isMavenCompatible)
}
object RepositoryHelpers object RepositoryHelpers
{ {
final case class Patterns(ivyPatterns: Seq[String], artifactPatterns: Seq[String], isMavenCompatible: Boolean) extends NotNull
{
private[xsbt] def mavenStyle(): Patterns = Patterns(ivyPatterns, artifactPatterns, true)
private[xsbt] def withIvys(patterns: Seq[String]): Patterns = Patterns(patterns ++ ivyPatterns, artifactPatterns, isMavenCompatible)
private[xsbt] def withArtifacts(patterns: Seq[String]): Patterns = Patterns(ivyPatterns, patterns ++ artifactPatterns, isMavenCompatible)
}
final case class SshConnection(authentication: Option[SshAuthentication], hostname: Option[String], port: Option[Int]) extends NotNull final case class SshConnection(authentication: Option[SshAuthentication], hostname: Option[String], port: Option[Int]) extends NotNull
{ {
def copy(authentication: Option[SshAuthentication]) = SshConnection(authentication, hostname, port) def copy(authentication: Option[SshAuthentication]) = SshConnection(authentication, hostname, port)
@ -63,7 +73,7 @@ object RepositoryHelpers
final case class PasswordAuthentication(user: String, password: String) extends SshAuthentication final case class PasswordAuthentication(user: String, password: String) extends SshAuthentication
final case class KeyFileAuthentication(keyfile: File, password: String) extends SshAuthentication final case class KeyFileAuthentication(keyfile: File, password: String) extends SshAuthentication
} }
import RepositoryHelpers.{Patterns, SshConnection, FileConfiguration} import RepositoryHelpers.{SshConnection, FileConfiguration}
import RepositoryHelpers.{KeyFileAuthentication, PasswordAuthentication, SshAuthentication} import RepositoryHelpers.{KeyFileAuthentication, PasswordAuthentication, SshAuthentication}
/** sbt interface to an Ivy repository based on patterns, which is most Ivy repositories.*/ /** sbt interface to an Ivy repository based on patterns, which is most Ivy repositories.*/
@ -283,12 +293,7 @@ object Configurations
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
private[xsbt] def defaultConfiguration(mavenStyle: Boolean) = private[xsbt] def defaultConfiguration(mavenStyle: Boolean) = if(mavenStyle) Configurations.Compile else Configurations.Default
{
val base = if(mavenStyle) Configurations.Compile else Configurations.Default
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. */
@ -337,6 +342,12 @@ object Artifact
Artifact(name, extract(name, defaultType), extract(name, defaultExtension), None, Nil, Some(file.toURI.toURL)) Artifact(name, extract(name, defaultType), extract(name, defaultExtension), None, Nil, Some(file.toURI.toURL))
} }
} }
final case class ModuleConfiguration(organization: String, name: String, revision: String, resolver: Resolver) extends NotNull
object ModuleConfiguration
{
def apply(org: String, resolver: Resolver): ModuleConfiguration = apply(org, "*", "*", resolver)
def apply(org: String, name: String, resolver: Resolver): ModuleConfiguration = ModuleConfiguration(org, name, "*", resolver)
}
/* /*
object Credentials object Credentials
{ {

View File

@ -14,34 +14,33 @@ object ComponentManagerTest extends Specification
} }
"throw an exception if 'file' is called for an empty component" in { "throw an exception if 'file' is called for an empty component" in {
withManager { manager => withManager { manager =>
createDirectory(manager.location(TestID)) manager.define(TestID, Nil)
( manager.file(TestID) ) must throwA[InvalidComponent] ( manager.file(TestID) ) must throwA[InvalidComponent]
} }
} }
"return the file for a single-file component" in { "return the file for a single-file component" in {
withManager { manager => withManager { manager =>
createFiles(manager, TestID, "a") match { case Seq(x) => val hash = defineFile(manager, TestID, "a")
manager.file(TestID).getAbsoluteFile must beEqualTo(x.getAbsoluteFile) checksum(manager.file(TestID)) must beEqualTo(hash)
}
} }
} }
"throw an exception if 'file' is called for multi-file component" in { "throw an exception if 'file' is called for multi-file component" in {
withManager { manager => withManager { manager =>
createFiles(manager, TestID, "a", "b") defineFiles(manager, TestID, "a", "b")
( manager.file(TestID) ) must throwA[InvalidComponent] ( manager.file(TestID) ) must throwA[InvalidComponent]
} }
} }
"return the files for a multi-file component" in { "return the files for a multi-file component" in {
withManager { manager => withManager { manager =>
val files = createFiles(manager, TestID, "a", "b") val hashes = defineFiles(manager, TestID, "a", "b")
manager.files(TestID) must haveTheSameElementsAs(files) checksum(manager.files(TestID)) must haveTheSameElementsAs(hashes)
} }
} }
"return the files for a single-file component" in { "return the files for a single-file component" in {
withManager { manager => withManager { manager =>
val files = createFiles(manager, TestID, "a") val hashes = defineFiles(manager, TestID, "a")
manager.files(TestID) must haveTheSameElementsAs(files) checksum(manager.files(TestID)) must haveTheSameElementsAs(hashes)
} }
} }
"throw an exception if 'files' is called for a non-existing component" in { "throw an exception if 'files' is called for a non-existing component" in {
@ -49,47 +48,33 @@ object ComponentManagerTest extends Specification
} }
"properly cache a file and then retrieve it to an unresolved component" in { "properly cache a file and then retrieve it to an unresolved component" in {
withManager { manager => withManager { definingManager =>
val file = createFile(manager, TestID, "a") val hash = defineFile(definingManager, TestID, "a")
val hash = checksum(file)
try try
{ {
manager.cache(TestID) definingManager.cache(TestID)
delete(manager.location(TestID)) withManager { usingManager =>
FileUtilities.listFiles(manager.location(TestID)).toList must haveSize(0) checksum(usingManager.file(TestID)) must beEqualTo(hash)
checksum(manager.file(TestID)) must beEqualTo(hash) }
} }
finally { manager.clearCache(TestID) } finally { definingManager.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 checksum(files: Iterable[File]): Seq[String] = files.map(checksum).toSeq
private def createFile(manager: ComponentManager, id: String, name: String): File = createFiles(manager, id, name).toList.head private def checksum(file: File): String = if(file.exists) ChecksumHelper.computeAsString(file, "sha1") else ""
private def createFiles(manager: ComponentManager, id: String, names: String*): Seq[File] = private def defineFile(manager: ComponentManager, id: String, name: String): String = createFile(manager, id, name)(checksum)
{ private def defineFiles(manager: ComponentManager, id: String, names: String*): Seq[String] = createFiles(manager, id, names : _*)(checksum)
val dir = manager.location(id) private def createFile[T](manager: ComponentManager, id: String, name: String)(f: File => T): T = createFiles(manager, id, name)(files => f(files.toList.head))
createDirectory(dir) private def createFiles[T](manager: ComponentManager, id: String, names: String*)(f: Seq[File] => T): T =
names.map { name => withTemporaryDirectory { dir =>
val testFile = new File(dir, name) val files = names.map(name => new File(dir, name) )
touch(testFile) files.foreach(writeRandomContent)
testFile manager.define(id, files)
f(files)
} }
} private def writeRandomContent(file: File) = FileUtilities.write(file, randomString)
private def randomString = "asdf"
private def withManager[T](f: ComponentManager => T): T = private def withManager[T](f: ComponentManager => T): T =
TestIvyLogger( logger => withTemporaryDirectory { temp => f(new ComponentManager(temp, logger)) } ) TestIvyLogger( logger => withTemporaryDirectory { temp => f(new ComponentManager(new xsbt.boot.ComponentProvider(temp), logger)) } )
} }