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] =
{