diff --git a/ivy/ComponentManager.scala b/ivy/ComponentManager.scala index fc1209758..d13459a67 100644 --- a/ivy/ComponentManager.scala +++ b/ivy/ComponentManager.scala @@ -1,7 +1,6 @@ package xsbt import java.io.File -import xsbti.Versions /** 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. @@ -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 * 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. */ 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 + "'") } /** 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(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. */ 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) } - 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'. */ def cache(id: String): Unit = IvyCache.cacheJar(sbtModuleID(id), file(id), log) def clearCache(id: String): Unit = IvyCache.clearCachedJar(sbtModuleID(id), log) diff --git a/ivy/ConvertResolver.scala b/ivy/ConvertResolver.scala index 51c3277eb..6625e604e 100644 --- a/ivy/ConvertResolver.scala +++ b/ivy/ConvertResolver.scala @@ -91,7 +91,7 @@ private object ConvertResolver setKeyFilePassword(password) } } - private def initializePatterns(resolver: AbstractPatternsBasedResolver, patterns: RepositoryHelpers.Patterns) + private def initializePatterns(resolver: AbstractPatternsBasedResolver, patterns: Patterns) { resolver.setM2compatible(patterns.isMavenCompatible) patterns.ivyPatterns.foreach(resolver.addIvyPattern) diff --git a/ivy/CustomXmlParser.scala b/ivy/CustomXmlParser.scala index 91ca60a79..218b324ff 100644 --- a/ivy/CustomXmlParser.scala +++ b/ivy/CustomXmlParser.scala @@ -17,8 +17,10 @@ import plugins.repository.url.URLResource private[xsbt] object CustomXmlParser extends XmlModuleDescriptorParser with NotNull { 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) = { super.setResource(new URLResource(url)) @@ -29,7 +31,6 @@ private[xsbt] object CustomXmlParser extends XmlModuleDescriptorParser with NotN override def setResource(res: Resource) {} override def setMd(md: DefaultModuleDescriptor) = super.setMd(md) override def parseDepsConfs(confs: String, dd: DefaultDependencyDescriptor) = super.parseDepsConfs(confs, dd) - override def getDefaultConf = super.getDefaultConf - override def setDefaultConf(conf: String) = super.setDefaultConf(conf) + override def getDefaultConf = defaultConfig.getOrElse(super.getDefaultConf) } } \ No newline at end of file diff --git a/ivy/Ivy.scala b/ivy/Ivy.scala index 3e45f790b..fee07b00b 100644 --- a/ivy/Ivy.scala +++ b/ivy/Ivy.scala @@ -8,6 +8,7 @@ import Artifact.{defaultExtension, defaultType} import java.io.File import org.apache.ivy.{core, plugins, util, Ivy} +import core.IvyPatternHelper import core.cache.DefaultRepositoryCacheManager import core.module.descriptor.{DefaultArtifact, DefaultDependencyArtifactDescriptor, MDArtifact} import core.module.descriptor.{DefaultDependencyDescriptor, DefaultModuleDescriptor, ModuleDescriptor} @@ -74,7 +75,7 @@ final class IvySbt(configuration: IvyConfiguration) finally { ivy.popContext() } } - final class Module(val moduleConfiguration: ModuleConfiguration) extends NotNull + final class Module(val moduleSettings: ModuleSettings) extends NotNull { def logger = configuration.log 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) = { val (baseModule, baseConfiguration) = - moduleConfiguration match + moduleSettings match { case ic: InlineConfiguration => configureInline(ic) case ec: EmptyConfiguration => configureEmpty(ec.module) case pc: PomConfiguration => readPom(pc.file, pc.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, baseConfiguration) } @@ -105,6 +106,7 @@ final class IvySbt(configuration: IvyConfiguration) IvySbt.addArtifacts(moduleID, artifacts) IvySbt.addDependencies(moduleID, dependencies, parser) + IvySbt.setModuleConfigurations(settings, moduleConfigurations) IvySbt.addMainArtifact(moduleID) (moduleID, parser.getDefaultConf) } @@ -126,7 +128,7 @@ final class IvySbt(configuration: IvyConfiguration) private def readIvyFile(ivyFile: File, validate: Boolean) = { val url = toURL(ivyFile) - val parser = new CustomXmlParser.CustomParser(settings) + val parser = new CustomXmlParser.CustomParser(settings, None) parser.setValidate(validate) parser.setSource(url) parser.parse() @@ -168,6 +170,20 @@ private object IvySbt settings.setDefaultResolver(newDefault.getName) 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]) { 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'. */ 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.setDefaultConf(defaultConfiguration) parser.setValidate(validate) parser.setInput(xml.getBytes) parser.parse() diff --git a/ivy/IvyActions.scala b/ivy/IvyActions.scala index 3b95062a9..f738a3912 100644 --- a/ivy/IvyActions.scala +++ b/ivy/IvyActions.scala @@ -27,7 +27,7 @@ object IvyActions { module.logger.info("Installing " + dependency) val options = new InstallOptions - options.setValidate(module.moduleConfiguration.validate) + options.setValidate(module.moduleSettings.validate) options.setTransitive(dependency.isTransitive) ivy.install(dependency.getDependencyRevisionId, from, to, options) } @@ -50,10 +50,8 @@ object IvyActions // todo: correct default configuration for extra dependencies 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) - val defaultConf = if(defaultConfiguration.contains("->")) defaultConfiguration else (defaultConfiguration + "->default(compile)") - parser.setDefaultConf(defaultConf) IvySbt.addDependencies(module, extraDependencies, parser) } private def getConfigurations(module: ModuleDescriptor, configurations: Option[Iterable[Configuration]]) = diff --git a/ivy/IvyCache.scala b/ivy/IvyCache.scala index cd6fdb2b2..9e9c4d9e4 100644 --- a/ivy/IvyCache.scala +++ b/ivy/IvyCache.scala @@ -40,19 +40,20 @@ object IvyCache /** Clears the cache of the jar for the given ID.*/ 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) } } /** 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 = + withCachedJar(id, log) { cachedFile => + 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 + * TODO: locking.*/ + def withCachedJar[T](id: ModuleID, log: IvyLogger)(f: File => T): T = { val cachedFile = try @@ -64,7 +65,7 @@ object IvyCache } 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.*/ def withDefaultCache[T](log: IvyLogger)(f: DefaultRepositoryCacheManager => T): T = @@ -82,7 +83,7 @@ object IvyCache { val local = Resolver.defaultLocal 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) } /** Creates a default jar artifact based on the given ID.*/ diff --git a/ivy/IvyConfigurations.scala b/ivy/IvyConfigurations.scala index 6bf0115e5..29cbd15ab 100644 --- a/ivy/IvyConfigurations.scala +++ b/ivy/IvyConfigurations.scala @@ -7,19 +7,20 @@ import java.io.File import scala.xml.NodeSeq 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 ivyScala: Option[IvyScala] } -final class IvyFileConfiguration(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 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 ModuleSettings 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 artifacts: Iterable[Artifact], val validate: Boolean) extends ModuleConfiguration -final class EmptyConfiguration(val module: ModuleID, val ivyScala: Option[IvyScala], 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 ModuleSettings object InlineConfiguration { def apply(module: ModuleID, dependencies: Iterable[ModuleID], artifacts: Iterable[Artifact]) = @@ -37,7 +38,7 @@ object InlineConfiguration else explicitConfigurations } -object ModuleConfiguration +object ModuleSettings { def apply(ivyScala: Option[IvyScala], validate: Boolean, module: => ModuleID)(baseDirectory: File, log: IvyLogger) = { diff --git a/ivy/IvyInterface.scala b/ivy/IvyInterface.scala index f62266cb1..f659321f2 100644 --- a/ivy/IvyInterface.scala +++ b/ivy/IvyInterface.scala @@ -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 classifier(c: String) = artifacts(Artifact(name, c)) 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 { @@ -30,6 +30,10 @@ object ModuleID 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 = 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 { @@ -40,14 +44,20 @@ sealed case class MavenRepository(name: String, root: String) extends Resolver 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 { - 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 { 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 KeyFileAuthentication(keyfile: File, password: String) extends SshAuthentication } -import RepositoryHelpers.{Patterns, SshConnection, FileConfiguration} +import RepositoryHelpers.{SshConnection, FileConfiguration} import RepositoryHelpers.{KeyFileAuthentication, PasswordAuthentication, SshAuthentication} /** 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 DefaultIvyConfiguration = defaultConfiguration(false) private[xsbt] def DefaultConfiguration(mavenStyle: Boolean) = if(mavenStyle) DefaultMavenConfiguration else DefaultIvyConfiguration - private[xsbt] def defaultConfiguration(mavenStyle: Boolean) = - { - val base = if(mavenStyle) Configurations.Compile else Configurations.Default - config(base.name + "->default(compile)") - } - + private[xsbt] def defaultConfiguration(mavenStyle: Boolean) = if(mavenStyle) Configurations.Compile else Configurations.Default 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. */ @@ -337,6 +342,12 @@ object Artifact 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 { diff --git a/ivy/src/test/scala/ComponentManagerTest.scala b/ivy/src/test/scala/ComponentManagerTest.scala index 5d88297f4..6928ab98a 100644 --- a/ivy/src/test/scala/ComponentManagerTest.scala +++ b/ivy/src/test/scala/ComponentManagerTest.scala @@ -14,34 +14,33 @@ object ComponentManagerTest extends Specification } "throw an exception if 'file' is called for an empty component" in { withManager { manager => - createDirectory(manager.location(TestID)) + manager.define(TestID, Nil) ( 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) - } + val hash = defineFile(manager, TestID, "a") + checksum(manager.file(TestID)) must beEqualTo(hash) } } "throw an exception if 'file' is called for multi-file component" in { withManager { manager => - createFiles(manager, TestID, "a", "b") + defineFiles(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) + val hashes = defineFiles(manager, TestID, "a", "b") + checksum(manager.files(TestID)) must haveTheSameElementsAs(hashes) } } "return the files for a single-file component" in { withManager { manager => - val files = createFiles(manager, TestID, "a") - manager.files(TestID) must haveTheSameElementsAs(files) + val hashes = defineFiles(manager, TestID, "a") + checksum(manager.files(TestID)) must haveTheSameElementsAs(hashes) } } "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 { - withManager { manager => - val file = createFile(manager, TestID, "a") - val hash = checksum(file) + withManager { definingManager => + val hash = defineFile(definingManager, TestID, "a") try { - manager.cache(TestID) - delete(manager.location(TestID)) - FileUtilities.listFiles(manager.location(TestID)).toList must haveSize(0) - checksum(manager.file(TestID)) must beEqualTo(hash) + definingManager.cache(TestID) + withManager { usingManager => + checksum(usingManager.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) } + finally { definingManager.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 checksum(files: Iterable[File]): Seq[String] = files.map(checksum).toSeq + private def checksum(file: File): String = if(file.exists) ChecksumHelper.computeAsString(file, "sha1") else "" + 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) + private def createFile[T](manager: ComponentManager, id: String, name: String)(f: File => T): T = createFiles(manager, id, name)(files => f(files.toList.head)) + private def createFiles[T](manager: ComponentManager, id: String, names: String*)(f: Seq[File] => T): T = + withTemporaryDirectory { dir => + val files = names.map(name => new File(dir, name) ) + files.foreach(writeRandomContent) + 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 = - TestIvyLogger( logger => withTemporaryDirectory { temp => f(new ComponentManager(temp, logger)) } ) + TestIvyLogger( logger => withTemporaryDirectory { temp => f(new ComponentManager(new xsbt.boot.ComponentProvider(temp), logger)) } ) } \ No newline at end of file