diff --git a/ivy/src/main/scala/sbt/UpdateOptions.scala b/ivy/src/main/scala/sbt/UpdateOptions.scala index 3f640867d..ba82e5053 100644 --- a/ivy/src/main/scala/sbt/UpdateOptions.scala +++ b/ivy/src/main/scala/sbt/UpdateOptions.scala @@ -14,6 +14,8 @@ import org.apache.ivy.core.settings.IvySettings final class UpdateOptions private[sbt] ( /** If set to CircularDependencyLevel.Error, halt the dependency resolution. */ val circularDependencyLevel: CircularDependencyLevel, + // If set to true, prioritize inter-project resolver + val interProjectFirst: Boolean, /** If set to true, check all resolvers for snapshots. */ val latestSnapshots: Boolean, /** If set to true, use consolidated resolution. */ @@ -24,6 +26,8 @@ final class UpdateOptions private[sbt] ( val resolverConverter: UpdateOptions.ResolverConverter) { def withCircularDependencyLevel(circularDependencyLevel: CircularDependencyLevel): UpdateOptions = copy(circularDependencyLevel = circularDependencyLevel) + def withInterProjectFirst(interProjectFirst: Boolean): UpdateOptions = + copy(interProjectFirst = interProjectFirst) def withLatestSnapshots(latestSnapshots: Boolean): UpdateOptions = copy(latestSnapshots = latestSnapshots) @deprecated("Use withCachedResolution instead.", "0.13.7") @@ -39,11 +43,13 @@ final class UpdateOptions private[sbt] ( private[sbt] def copy( circularDependencyLevel: CircularDependencyLevel = this.circularDependencyLevel, + interProjectFirst: Boolean = this.interProjectFirst, latestSnapshots: Boolean = this.latestSnapshots, consolidatedResolution: Boolean = this.consolidatedResolution, cachedResolution: Boolean = this.cachedResolution, resolverConverter: UpdateOptions.ResolverConverter = this.resolverConverter): UpdateOptions = new UpdateOptions(circularDependencyLevel, + interProjectFirst, latestSnapshots, consolidatedResolution, cachedResolution, @@ -52,6 +58,7 @@ final class UpdateOptions private[sbt] ( override def equals(o: Any): Boolean = o match { case o: UpdateOptions => this.circularDependencyLevel == o.circularDependencyLevel && + this.interProjectFirst == o.interProjectFirst && this.latestSnapshots == o.latestSnapshots && this.cachedResolution == o.cachedResolution && this.resolverConverter == o.resolverConverter @@ -62,6 +69,7 @@ final class UpdateOptions private[sbt] ( { var hash = 1 hash = hash * 31 + this.circularDependencyLevel.## + hash = hash * 31 + this.interProjectFirst.## hash = hash * 31 + this.latestSnapshots.## hash = hash * 31 + this.cachedResolution.## hash = hash * 31 + this.resolverConverter.## @@ -75,6 +83,7 @@ object UpdateOptions { def apply(): UpdateOptions = new UpdateOptions( circularDependencyLevel = CircularDependencyLevel.Warn, + interProjectFirst = true, latestSnapshots = true, consolidatedResolution = false, cachedResolution = false, diff --git a/ivy/src/main/scala/sbt/ivyint/SbtChainResolver.scala b/ivy/src/main/scala/sbt/ivyint/SbtChainResolver.scala index 7323f4aec..cc6f67816 100644 --- a/ivy/src/main/scala/sbt/ivyint/SbtChainResolver.scala +++ b/ivy/src/main/scala/sbt/ivyint/SbtChainResolver.scala @@ -112,6 +112,7 @@ private[sbt] case class SbtChainResolver( else resolvedOrCached // Cast resolvers to something useful. TODO - we dropping anything here? val resolvers = getResolvers.toArray.toVector collect { case x: DependencyResolver => x } + val interProjResolver = resolvers find { x => x.getName == ProjectResolver.InterProject } // Here we do an attempt to resolve the artifact from each of the resolvers in the chain. // - If we have a return value already, AND isReturnFirst is true AND useLatest is false, we DO NOT resolve anything @@ -119,7 +120,7 @@ private[sbt] case class SbtChainResolver( // RETURNS: Left -> Error // Right -> Some(resolved module) // Found in this resolver, can use this result. // Right -> None // Do not use this resolver - val results = resolvers map { x => + lazy val results = resolvers map { x => // if the revision is cached and isReturnFirst is set, don't bother hitting any resolvers, just return None for this guy. if (isReturnFirst && temp.isDefined && !useLatest) Right(None) else { @@ -150,50 +151,57 @@ private[sbt] case class SbtChainResolver( } } } - val errors = results collect { case Left(e) => e } - val foundRevisions: Vector[(ResolvedModuleRevision, DependencyResolver)] = results collect { case Right(Some(x)) => x } - val sorted = - if (useLatest) (foundRevisions.sortBy { - case (rmr, resolver) => - Message.warn(s"Sorrting results from $rmr, using ${rmr.getPublicationDate} and ${rmr.getDescriptor.getPublicationDate}") - // Just issue warning about issues with publication date, and fake one on it for now. - Option(rmr.getPublicationDate) orElse Option(rmr.getDescriptor.getPublicationDate) match { - case None => - (resolver.findIvyFileRef(dd, data), rmr.getDescriptor) match { - case (null, _) => - // In this instance, the dependency is specified by a direct URL or some other sort of "non-ivy" file - if (dd.isChanging) - Message.warn(s"Resolving a changing dependency (${rmr.getId}) with no ivy/pom file!, resolution order is undefined!") - 0L - case (ivf, dmd: DefaultModuleDescriptor) => - val lmd = new java.util.Date(ivf.getLastModified) - Message.debug(s"Getting no publication date from resolver: ${resolver} for ${rmr.getId}, setting to: ${lmd}") - dmd.setPublicationDate(lmd) - ivf.getLastModified - case _ => - Message.warn(s"Getting null publication date from resolver: ${resolver} for ${rmr.getId}, resolution order is undefined!") - 0L - } - case Some(date) => // All other cases ok - date.getTime - } - }).reverse.headOption map { - case (rmr, resolver) => - Message.warn(s"Choosing $resolver for ${rmr.getId}") - // Now that we know the real latest revision, let's force Ivy to use it - val artifactOpt = findFirstArtifactRef(rmr.getDescriptor, dd, data, resolver) - artifactOpt match { - case Some(artifactRef) => - val systemMd = toSystem(rmr.getDescriptor) - getRepositoryCacheManager.cacheModuleDescriptor(resolver, artifactRef, - toSystem(dd), systemMd.getAllArtifacts.head, None.orNull, getCacheOptions(data)) - case None => // do nothing. There are modules without artifacts - } - rmr - } - else foundRevisions.reverse.headOption map { _._1 } // we have to reverse because resolvers are hit in reverse order. + lazy val errors = results collect { case Left(e) => e } + // If the value is arleady in cache, SORTED will be a Seq(None, None, ...) which means we'll fall over to the prevously cached or resolved version. - val mrOpt: Option[ResolvedModuleRevision] = sorted orElse resolvedOrCached + val mrOpt: Option[ResolvedModuleRevision] = { + val interProj: Option[ResolvedModuleRevision] = + if (updateOptions.interProjectFirst) interProjResolver flatMap { x => Option(x.getDependency(dd, data)) } + else None + def foundRevisions: Vector[(ResolvedModuleRevision, DependencyResolver)] = results collect { case Right(Some(x)) => x } + def sorted = + if (useLatest) (foundRevisions.sortBy { + case (rmr, resolver) => + Message.warn(s"Sorrting results from $rmr, using ${rmr.getPublicationDate} and ${rmr.getDescriptor.getPublicationDate}") + // Just issue warning about issues with publication date, and fake one on it for now. + Option(rmr.getPublicationDate) orElse Option(rmr.getDescriptor.getPublicationDate) match { + case None => + (resolver.findIvyFileRef(dd, data), rmr.getDescriptor) match { + case (null, _) => + // In this instance, the dependency is specified by a direct URL or some other sort of "non-ivy" file + if (dd.isChanging) + Message.warn(s"Resolving a changing dependency (${rmr.getId}) with no ivy/pom file!, resolution order is undefined!") + 0L + case (ivf, dmd: DefaultModuleDescriptor) => + val lmd = new java.util.Date(ivf.getLastModified) + Message.debug(s"Getting no publication date from resolver: ${resolver} for ${rmr.getId}, setting to: ${lmd}") + dmd.setPublicationDate(lmd) + ivf.getLastModified + case _ => + Message.warn(s"Getting null publication date from resolver: ${resolver} for ${rmr.getId}, resolution order is undefined!") + 0L + } + case Some(date) => // All other cases ok + date.getTime + } + }).reverse.headOption map { + case (rmr, resolver) => + Message.warn(s"Choosing $resolver for ${rmr.getId}") + // Now that we know the real latest revision, let's force Ivy to use it + val artifactOpt = findFirstArtifactRef(rmr.getDescriptor, dd, data, resolver) + artifactOpt match { + case Some(artifactRef) => + val systemMd = toSystem(rmr.getDescriptor) + getRepositoryCacheManager.cacheModuleDescriptor(resolver, artifactRef, + toSystem(dd), systemMd.getAllArtifacts.head, None.orNull, getCacheOptions(data)) + case None => // do nothing. There are modules without artifacts + } + rmr + } + else foundRevisions.reverse.headOption map { _._1 } // we have to reverse because resolvers are hit in reverse order. + + interProj orElse sorted orElse resolvedOrCached + } mrOpt match { case None if errors.size == 1 => errors.head match { diff --git a/notes/0.13.10.markdown b/notes/0.13.10.markdown index d78f348e9..666d9f9cc 100644 --- a/notes/0.13.10.markdown +++ b/notes/0.13.10.markdown @@ -66,10 +66,16 @@ [2324]: https://github.com/sbt/sbt/issues/2324 [2325]: https://github.com/sbt/sbt/pull/2325 [2336]: https://github.com/sbt/sbt/issues/2336 + [1514]: https://github.com/sbt/sbt/issues/1514 + [1616]: https://github.com/sbt/sbt/issues/1616 + [2313]: https://github.com/sbt/sbt/pull/2313 ### Fixes with compatibility implications - sbt 0.13.10 adds a new setting `useJCenter`, which is set to `false` by default. When set to `true`, JCenter will be placed as the first external resolver to find library dependencies. [#2217][2217] by [@eed3si9n][@eed3si9n] +- Adds `withInterProjectFirst` to the update option, which is enabled by default. When set to `true`, `inter-project` resolver will be prioritized above all resolvers and Ivy cache. [#1827][1827] by [@eed3si9n][@eed3si9n] +- Fixes update option's `withLatestSnapshots` so it handles modules without an artifact. This flag will be enabled by default. + [#1514][1514]/[#1616][1616]/[#2313][2313] by [@eed3si9n][@eed3si9n] - sbt will no longer pass `-J` options to the local Java compiler. [#1968][1968]/[#2272][2272] by [@Duhemm][@Duhemm] ### Improvements diff --git a/notes/0.13.10/fix-snapshots.md b/notes/0.13.10/fix-snapshots.md deleted file mode 100644 index 59e946757..000000000 --- a/notes/0.13.10/fix-snapshots.md +++ /dev/null @@ -1,14 +0,0 @@ - - [@eed3si9n]: https://github.com/eed3si9n - [1514]: https://github.com/sbt/sbt/issues/1514 - [1616]: https://github.com/sbt/sbt/issues/1616 - [2313]: https://github.com/sbt/sbt/pull/2313 - -### Fixes with compatibility implications - -- Fixes update option's `withLatestSnapshots` so it handles modules without an artifact. This flag will be enabled by default. - [#1514][1514]/[#1616][1616]/[#2313][2313] by [@eed3si9n][@eed3si9n] - -### Improvements - -### Bug fixes