diff --git a/ivy/src/main/scala/sbt/ConvertResolver.scala b/ivy/src/main/scala/sbt/ConvertResolver.scala index 91fa137a2..58ba6d4ac 100644 --- a/ivy/src/main/scala/sbt/ConvertResolver.scala +++ b/ivy/src/main/scala/sbt/ConvertResolver.scala @@ -3,6 +3,7 @@ */ package sbt +import java.net.URL import java.util.Collections import org.apache.ivy.{core,plugins} import core.module.id.ModuleRevisionId @@ -11,6 +12,8 @@ import core.resolve.ResolveData import core.settings.IvySettings import plugins.resolver.{BasicResolver, DependencyResolver, IBiblioResolver} import plugins.resolver.{AbstractPatternsBasedResolver, AbstractSshBasedResolver, FileSystemResolver, SFTPResolver, SshResolver, URLResolver} +import plugins.repository.url.{URLRepository => URLRepo} +import plugins.repository.file.{FileRepository => FileRepo, FileResource} private object ConvertResolver { @@ -29,6 +32,7 @@ private object ConvertResolver } } val resolver = new PluginCapableResolver + resolver.setRepository(new LocalIfFileRepo) initializeMavenStyle(resolver, repo.name, repo.root) resolver.setPatterns() // has to be done after initializeMavenStyle, which calls methods that overwrite the patterns resolver @@ -128,4 +132,16 @@ private object ConvertResolver patterns.ivyPatterns.foreach(p => resolver.addIvyPattern(settings substitute p)) patterns.artifactPatterns.foreach(p => resolver.addArtifactPattern(settings substitute p)) } + /** A custom Ivy URLRepository that returns FileResources for file URLs. + * This allows using the artifacts from the Maven local repository instead of copying them to the Ivy cache. */ + private[this] final class LocalIfFileRepo extends URLRepo { + private[this] val repo = new FileRepo + override def getResource(source: String) = { + val url = new URL(source) + if(url.getProtocol == IO.FileScheme) + new FileResource(repo, IO.toFile(url)) + else + super.getResource(source) + } + } } diff --git a/sbt/src/sbt-test/dependency-management/mvn-local/build.sbt b/sbt/src/sbt-test/dependency-management/mvn-local/build.sbt new file mode 100644 index 000000000..f29ff5029 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/mvn-local/build.sbt @@ -0,0 +1,28 @@ +organization in ThisBuild := "org.example" + +version in ThisBuild := "1.0-SNAPSHOT" + + +lazy val main = project.settings( + uniqueName, + libraryDependencies += (projectID in library).value +) + +lazy val library = project.settings(uniqueName) + +def uniqueName = + name := (name.value + "-" + randomSuffix( (baseDirectory in ThisBuild).value)) + +// better long-term approach to a clean cache/local +// would be to not use the actual ~/.m2/repository +def randomSuffix(base: File) = { + // need to persist it so that it doesn't change across reloads + val persist = base / "suffix" + if(persist.exists) + IO.read(persist) + else { + val s = Hash.halfHashString(System.currentTimeMillis.toString) + IO.write(persist, s) + s + } +} diff --git a/sbt/src/sbt-test/dependency-management/mvn-local/changes/libA.scala b/sbt/src/sbt-test/dependency-management/mvn-local/changes/libA.scala new file mode 100644 index 000000000..6ea495dda --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/mvn-local/changes/libA.scala @@ -0,0 +1 @@ +object A { val x = 3 } diff --git a/sbt/src/sbt-test/dependency-management/mvn-local/changes/libDeps.sbt b/sbt/src/sbt-test/dependency-management/mvn-local/changes/libDeps.sbt new file mode 100644 index 000000000..f61fd2191 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/mvn-local/changes/libDeps.sbt @@ -0,0 +1 @@ +libraryDependencies += "junit" % "junit" % "4.11" diff --git a/sbt/src/sbt-test/dependency-management/mvn-local/changes/mainB1.scala b/sbt/src/sbt-test/dependency-management/mvn-local/changes/mainB1.scala new file mode 100644 index 000000000..53f6f4eea --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/mvn-local/changes/mainB1.scala @@ -0,0 +1 @@ +object B { val y = A.x } diff --git a/sbt/src/sbt-test/dependency-management/mvn-local/changes/mainB2.scala b/sbt/src/sbt-test/dependency-management/mvn-local/changes/mainB2.scala new file mode 100644 index 000000000..65b80c3bd --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/mvn-local/changes/mainB2.scala @@ -0,0 +1,3 @@ +import org.junit.Test + +object B { val y = A.x } diff --git a/sbt/src/sbt-test/dependency-management/mvn-local/changes/mvnLocal.sbt b/sbt/src/sbt-test/dependency-management/mvn-local/changes/mvnLocal.sbt new file mode 100644 index 000000000..3e70dbffe --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/mvn-local/changes/mvnLocal.sbt @@ -0,0 +1 @@ +resolvers := (Resolver.mavenLocal +: resolvers.value) diff --git a/sbt/src/sbt-test/dependency-management/mvn-local/test b/sbt/src/sbt-test/dependency-management/mvn-local/test new file mode 100644 index 000000000..a6b7f81d1 --- /dev/null +++ b/sbt/src/sbt-test/dependency-management/mvn-local/test @@ -0,0 +1,44 @@ +> library/publishM2 + +# should fail because local Maven repository not added yet +-> main/update + +# should succeed now that local Maven repository is added +$ copy-file changes/mvnLocal.sbt main/build.sbt +> reload +> main/update + +# should succeed because 'main' doesn't actually use anything from the library yet +> main/compile + +$ copy-file changes/mainB1.scala main/B.scala +# should fail because there is nothing in the library jar and 'main' actually uses it now +-> main/compile + +$ copy-file changes/libA.scala library/A.scala +> library/publishM2 +# should succeed even without 'update' because Ivy should use the jar from the origin and not copy it to its cache +> main/compile + +# should still succeed with an explicit 'update' +> main/update +> main/compile + + + +# update B.scala to depend on a dependency that 'library' doesn't declare yet +$ delete main/B.scala +$ copy-file changes/mainB2.scala main/B.scala + +# it should fail to compile because that dependency isn't declared +-> main/compile + + +# add a dependency to 'library' and use it from 'main' +# this will require the metadata to be updated correctly and not just the artifact +$ copy-file changes/libDeps.sbt library/build.sbt +> reload +> library/publishM2 + +# should succeed now that the dependency is there +> main/compile diff --git a/util/io/src/main/scala/sbt/IO.scala b/util/io/src/main/scala/sbt/IO.scala index 9534eea1d..ed976572b 100644 --- a/util/io/src/main/scala/sbt/IO.scala +++ b/util/io/src/main/scala/sbt/IO.scala @@ -29,7 +29,7 @@ object IO /** The size of the byte or char buffer used in various methods.*/ private val BufferSize = 8192 /** File scheme name */ - private[this] val FileScheme = "file" + private[sbt] val FileScheme = "file" /** The newline string for this system, as obtained by the line.separator system property. */ val Newline = System.getProperty("line.separator")