From 7109bc963771b1023334af4d0e90536dd6af128d Mon Sep 17 00:00:00 2001 From: Mark Harrah Date: Wed, 11 Apr 2012 22:40:45 -0400 Subject: [PATCH] configuration via ivysettings.xml: use URI instead of URL and make 'inter-project' resolver available for multi-project builds. ref #416 --- ivy/Ivy.scala | 20 ++++++++++++++++++- ivy/IvyConfigurations.scala | 9 +++++---- main/Defaults.scala | 19 ++++++++++-------- main/actions/CacheIvy.scala | 6 +----- .../changes/ivysettings.xml | 11 ++++++++++ .../ivy-settings-multi-a/dep/D.scala | 3 +++ .../ivy-settings-multi-a/project/P.scala | 18 +++++++++++++++++ .../ivy-settings-multi-a/test | 3 +++ .../ivy-settings-multi-a/use/U.scala | 5 +++++ .../ivy-settings-multi-a/use/ivysettings.xml | 10 ++++++++++ .../ivy-settings-multi-b/A.scala | 1 + .../ivy-settings-multi-b/b/B.scala | 1 + .../ivy-settings-multi-b/ivysettings.xml | 13 ++++++++++++ .../ivy-settings-multi-b/project/Build.scala | 8 ++++++++ .../ivy-settings-multi-b/test | 1 + util/io/Hash.scala | 9 ++++++++- 16 files changed, 118 insertions(+), 19 deletions(-) create mode 100644 sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/changes/ivysettings.xml create mode 100644 sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/dep/D.scala create mode 100644 sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/project/P.scala create mode 100644 sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/test create mode 100644 sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/use/U.scala create mode 100644 sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/use/ivysettings.xml create mode 100644 sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/A.scala create mode 100644 sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/b/B.scala create mode 100644 sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/ivysettings.xml create mode 100644 sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/project/Build.scala create mode 100644 sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/test diff --git a/ivy/Ivy.scala b/ivy/Ivy.scala index 002618fad..dba07c443 100644 --- a/ivy/Ivy.scala +++ b/ivy/Ivy.scala @@ -6,6 +6,7 @@ package sbt import Resolver.PluginPattern import java.io.File +import java.net.URI import java.util.concurrent.Callable import java.util.{Collection, Collections => CS} import CS.singleton @@ -65,7 +66,9 @@ final class IvySbt(val configuration: IvyConfiguration) CustomPomParser.registerDefault configuration match { - case e: ExternalIvyConfiguration => is.load(e.url) + case e: ExternalIvyConfiguration => + IvySbt.addResolvers(e.extraResolvers, is, configuration.log) + IvySbt.loadURI(is, e.uri) case i: InlineIvyConfiguration => is.setVariable("ivy.checksums", i.checksums mkString ",") i.paths.ivyHome foreach is.setDefaultIvyUserDir @@ -198,6 +201,14 @@ private object IvySbt def defaultIvyConfiguration(project: File) = new File(project, DefaultIvyConfigFilename) def defaultPOM(project: File) = new File(project, DefaultMavenFilename) + def loadURI(is: IvySettings, uri: URI) + { + if(uri.getScheme == "file") + is.load(new File(uri)) // IVY-1114 + else + is.load(uri.toURL) + } + /** Sets the resolvers for 'settings' to 'resolvers'. This is done by creating a new chain and making it the default. * 'other' is for resolvers that should be in a different chain. These are typically used for publishing or other actions. */ private def setResolvers(settings: IvySettings, resolvers: Seq[Resolver], other: Seq[Resolver], localOnly: Boolean, log: Logger) @@ -236,6 +247,13 @@ private object IvySbt } newDefault } + def addResolvers(resolvers: Seq[Resolver], settings: IvySettings, log: Logger) + { + for(r <- resolvers) { + log.debug("\t" + r) + settings.addResolver(ConvertResolver(r)(settings, log)) + } + } /** A hack to detect if the given artifact is an automatically generated request for a classifier, * as opposed to a user-initiated declaration. It relies on Ivy prefixing classifier with m:, while sbt uses e:. * Clearly, it would be better to have an explicit option in Ivy to control this.*/ diff --git a/ivy/IvyConfigurations.scala b/ivy/IvyConfigurations.scala index c3999ea6f..d2a5c958c 100644 --- a/ivy/IvyConfigurations.scala +++ b/ivy/IvyConfigurations.scala @@ -4,7 +4,7 @@ package sbt import java.io.File -import java.net.URL +import java.net.{URI,URL} import scala.xml.NodeSeq final class IvyPaths(val baseDirectory: File, val ivyHome: Option[File]) @@ -28,20 +28,21 @@ final class InlineIvyConfiguration(val paths: IvyPaths, val resolvers: Seq[Resol def withBase(newBase: File) = new InlineIvyConfiguration(paths.withBase(newBase), resolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, log) def changeResolvers(newResolvers: Seq[Resolver]) = new InlineIvyConfiguration(paths, newResolvers, otherResolvers, moduleConfigurations, localOnly, lock, checksums, log) } -final class ExternalIvyConfiguration(val baseDirectory: File, val url: URL, val lock: Option[xsbti.GlobalLock], val log: Logger) extends IvyConfiguration +final class ExternalIvyConfiguration(val baseDirectory: File, val uri: URI, val lock: Option[xsbti.GlobalLock], val extraResolvers: Seq[Resolver], val log: Logger) extends IvyConfiguration { type This = ExternalIvyConfiguration - def withBase(newBase: File) = new ExternalIvyConfiguration(newBase, url, lock, log) + def withBase(newBase: File) = new ExternalIvyConfiguration(newBase, uri, lock, extraResolvers, log) } object ExternalIvyConfiguration { - def apply(baseDirectory: File, file: File, lock: Option[xsbti.GlobalLock], log: Logger) = new ExternalIvyConfiguration(baseDirectory, file.toURI.toURL, lock, log) + def apply(baseDirectory: File, file: File, lock: Option[xsbti.GlobalLock], log: Logger) = new ExternalIvyConfiguration(baseDirectory, file.toURI, lock, Nil, log) } object IvyConfiguration { /** Called to configure Ivy when inline resolvers are not specified. * This will configure Ivy with an 'ivy-settings.xml' file if there is one or else use default resolvers.*/ + @deprecated("Explicitly use either external or inline configuration.", "0.12.0") def apply(paths: IvyPaths, lock: Option[xsbti.GlobalLock], localOnly: Boolean, checksums: Seq[String], log: Logger): IvyConfiguration = { log.debug("Autodetecting configuration.") diff --git a/main/Defaults.scala b/main/Defaults.scala index 0a4e86f89..b372fdfd4 100755 --- a/main/Defaults.scala +++ b/main/Defaults.scala @@ -21,7 +21,7 @@ package sbt import org.apache.ivy.core.module.{descriptor, id} import descriptor.ModuleDescriptor, id.ModuleRevisionId import java.io.File - import java.net.URL + import java.net.{URI,URL} import java.util.concurrent.Callable import sbinary.DefaultProtocol.StringFormat import Cache.seqFormat @@ -1251,14 +1251,17 @@ trait BuildExtra extends BuildCommon def seq(settings: Setting[_]*): SettingsDefinition = new Project.SettingList(settings) - def externalIvySettings(file: Initialize[File] = baseDirectory / "ivysettings.xml"): Setting[Task[IvyConfiguration]] = - externalIvySettingsUrl(file(_.toURI.toURL)) - def externalIvySettings(url: URL): Setting[Task[IvyConfiguration]] = externalIvySettingsUrl(new Project.Value(() => url)) - private def externalIvySettingsUrl(url: Initialize[URL]): Setting[Task[IvyConfiguration]] = + def externalIvySettings(file: Initialize[File] = baseDirectory / "ivysettings.xml", addMultiResolver: Boolean = true): Setting[Task[IvyConfiguration]] = + externalIvySettingsURI(file(_.toURI), addMultiResolver) + def externalIvySettingsURL(url: URL, addMultiResolver: Boolean = true): Setting[Task[IvyConfiguration]] = + externalIvySettingsURI(Project.value(url.toURI), addMultiResolver) + def externalIvySettingsURI(uri: Initialize[URI], addMultiResolver: Boolean = true): Setting[Task[IvyConfiguration]] = { - val other = (baseDirectory, appConfiguration, streams).identityMap - ivyConfiguration <<= (url zipWith other) { case (u, otherTask) => - otherTask map { case (base, app, s) => new ExternalIvyConfiguration(base, u, Some(lock(app)), s.log) } + val other = (baseDirectory, appConfiguration, projectResolver, streams).identityMap + ivyConfiguration <<= (uri zipWith other) { case (u, otherTask) => + otherTask map { case (base, app, pr, s) => + val extraResolvers = if(addMultiResolver) pr :: Nil else Nil + new ExternalIvyConfiguration(base, u, Some(lock(app)), extraResolvers, s.log) } } } def externalIvyFile(file: Initialize[File] = baseDirectory / "ivy.xml", iScala: Initialize[Option[IvyScala]] = ivyScala): Setting[Task[ModuleSettings]] = diff --git a/main/actions/CacheIvy.scala b/main/actions/CacheIvy.scala index e685b7eb8..bc73bd550 100644 --- a/main/actions/CacheIvy.scala +++ b/main/actions/CacheIvy.scala @@ -186,11 +186,7 @@ object CacheIvy implicit def fileConfToHL = (f: FileConfiguration) => f.isLocal :+: f.isTransactional :+: HNil implicit def externalIvyConfigurationToHL = (e: ExternalIvyConfiguration) => - exists(e.baseDirectory) :+: - (e.url match { - case u: URL if u.getProtocol == "file" => Hash(u) - case u: URL => Hash(u.toURI.normalize.toString) - }) :+: HNil + exists(e.baseDirectory) :+: Hash.contentsIfLocal(e.uri) :+: HNil } import L1._ diff --git a/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/changes/ivysettings.xml b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/changes/ivysettings.xml new file mode 100644 index 000000000..7a132acdd --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/changes/ivysettings.xml @@ -0,0 +1,11 @@ + + + + + + + + + + + diff --git a/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/dep/D.scala b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/dep/D.scala new file mode 100644 index 000000000..9c83ff32a --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/dep/D.scala @@ -0,0 +1,3 @@ +object D { + val x = 3 +} \ No newline at end of file diff --git a/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/project/P.scala b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/project/P.scala new file mode 100644 index 000000000..a12c276fe --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/project/P.scala @@ -0,0 +1,18 @@ +import sbt._ +import Keys._ + +object B extends Build +{ + lazy val dep = Project("dep", file("dep")) settings( baseSettings : _*) settings( + organization := "org.example", + version := "1.0" + ) + lazy val use = Project("use", file("use")) dependsOn(dep) settings(baseSettings : _*) settings( + libraryDependencies += "junit" % "junit" % "4.5", + externalIvySettings() + ) + lazy val baseSettings = Seq( + autoScalaLibrary := false, + unmanagedJars in Compile <++= scalaInstance map (_.jars) + ) +} diff --git a/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/test b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/test new file mode 100644 index 000000000..c1d7604e8 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/test @@ -0,0 +1,3 @@ +-> use/compile +$ copy-file changes/ivysettings.xml use/ivysettings.xml +> use/compile diff --git a/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/use/U.scala b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/use/U.scala new file mode 100644 index 000000000..8b0051d43 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/use/U.scala @@ -0,0 +1,5 @@ +import junit._ + +object U { + val x = D.x +} diff --git a/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/use/ivysettings.xml b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/use/ivysettings.xml new file mode 100644 index 000000000..02d348c12 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-a/use/ivysettings.xml @@ -0,0 +1,10 @@ + + + + + + + + + + diff --git a/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/A.scala b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/A.scala new file mode 100644 index 000000000..72a7e191e --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/A.scala @@ -0,0 +1 @@ +object A { val x = B.x } \ No newline at end of file diff --git a/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/b/B.scala b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/b/B.scala new file mode 100644 index 000000000..81661ea1b --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/b/B.scala @@ -0,0 +1 @@ +object B { val x = 3 } \ No newline at end of file diff --git a/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/ivysettings.xml b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/ivysettings.xml new file mode 100644 index 000000000..3c60b99b1 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/ivysettings.xml @@ -0,0 +1,13 @@ + + + + + + + + + + + + + \ No newline at end of file diff --git a/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/project/Build.scala b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/project/Build.scala new file mode 100644 index 000000000..a08ff557b --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/project/Build.scala @@ -0,0 +1,8 @@ +import sbt._ +import Keys._ + +object Build extends Build +{ + lazy val a = Project("a", file(".")) settings(externalIvySettings()) dependsOn(b) + lazy val b = Project("b", file("b")) settings(externalIvySettings( (baseDirectory in ThisBuild) / "ivysettings.xml" )) +} \ No newline at end of file diff --git a/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/test b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/test new file mode 100644 index 000000000..73a68203f --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/ivy-settings-multi-b/test @@ -0,0 +1 @@ +> compile \ No newline at end of file diff --git a/util/io/Hash.scala b/util/io/Hash.scala index 71d661530..dac4a1579 100644 --- a/util/io/Hash.scala +++ b/util/io/Hash.scala @@ -4,7 +4,7 @@ package sbt import java.io.{ByteArrayInputStream, File, InputStream} -import java.net.URL +import java.net.{URI,URL} object Hash { @@ -45,6 +45,13 @@ object Hash def apply(file: File): Array[Byte] = Using.fileInputStream(file)(apply) /** Calculates the SHA-1 hash of the given resource.*/ def apply(url: URL): Array[Byte] = Using.urlInputStream(url)(apply) + + /** If the URI represents a local file (the scheme is "file"), + * this method calculates the SHA-1 hash of the contents of that file. + * Otherwise, this methods calculates the SHA-1 hash of the normalized string representation of the URI.*/ + def contentsIfLocal(uri: URI): Array[Byte] = + if(uri.getScheme == "file") apply(uri.toURL) else apply(uri.normalize.toString) + /** Calculates the SHA-1 hash of the given stream, closing it when finished.*/ def apply(stream: InputStream): Array[Byte] = {