mirror of https://github.com/sbt/sbt.git
Turned sbt launcher into a general Scala application launcher as described in launch.specification
This commit is contained in:
parent
b6b7fe2f9b
commit
abd06a17c5
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
||||||
|
|
|
||||||
|
|
@ -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)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -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()
|
||||||
|
|
|
||||||
|
|
@ -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]]) =
|
||||||
|
|
|
||||||
|
|
@ -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.*/
|
||||||
|
|
|
||||||
|
|
@ -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) =
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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
|
||||||
{
|
{
|
||||||
|
|
|
||||||
|
|
@ -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)) } )
|
||||||
}
|
}
|
||||||
Loading…
Reference in New Issue