diff --git a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtChainResolver.scala b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtChainResolver.scala index 031998f36..c325d4af3 100644 --- a/librarymanagement/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtChainResolver.scala +++ b/librarymanagement/src/main/scala/sbt/internal/librarymanagement/ivyint/SbtChainResolver.scala @@ -115,6 +115,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 @@ -122,7 +123,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 { @@ -153,54 +154,61 @@ 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 None if resolver.getName == "inter-project" => // do nothing - case None if resolver.isInstanceOf[CustomMavenResolver] => - // do nothing for now.... - // We want to see if the maven caching is sufficient and we do not need to duplicate within the ivy cache... - case None => throw new RuntimeException(s"\t${resolver.getName}: no ivy file nor artifact found for $rmr") - case Some(artifactRef) => - val systemMd = toSystem(rmr.getDescriptor) - getRepositoryCacheManager.cacheModuleDescriptor(resolver, artifactRef, - toSystem(dd), systemMd.getAllArtifacts.head, None.orNull, getCacheOptions(data)) - } - 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 None if resolver.getName == "inter-project" => // do nothing + case None if resolver.isInstanceOf[CustomMavenResolver] => + // do nothing for now.... + // We want to see if the maven caching is sufficient and we do not need to duplicate within the ivy cache... + case None => throw new RuntimeException(s"\t${resolver.getName}: no ivy file nor artifact found for $rmr") + case Some(artifactRef) => + val systemMd = toSystem(rmr.getDescriptor) + getRepositoryCacheManager.cacheModuleDescriptor(resolver, artifactRef, + toSystem(dd), systemMd.getAllArtifacts.head, None.orNull, getCacheOptions(data)) + } + 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 { @@ -305,4 +313,4 @@ private[sbt] case class SbtChainResolver( oldLatest case _ => None } -} \ No newline at end of file +} diff --git a/librarymanagement/src/main/scala/sbt/librarymanagement/UpdateOptions.scala b/librarymanagement/src/main/scala/sbt/librarymanagement/UpdateOptions.scala index be2ad412f..8b70c3971 100644 --- a/librarymanagement/src/main/scala/sbt/librarymanagement/UpdateOptions.scala +++ b/librarymanagement/src/main/scala/sbt/librarymanagement/UpdateOptions.scala @@ -15,6 +15,8 @@ import sbt.util.Logger 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. @@ -26,6 +28,8 @@ final class UpdateOptions private[sbt] ( ) { 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") @@ -45,6 +49,7 @@ 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, @@ -52,6 +57,7 @@ final class UpdateOptions private[sbt] ( ): UpdateOptions = new UpdateOptions( circularDependencyLevel, + interProjectFirst, latestSnapshots, consolidatedResolution, cachedResolution, @@ -61,6 +67,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 @@ -71,6 +78,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.## @@ -84,6 +92,7 @@ object UpdateOptions { def apply(): UpdateOptions = new UpdateOptions( circularDependencyLevel = CircularDependencyLevel.Warn, + interProjectFirst = true, latestSnapshots = false, consolidatedResolution = false, cachedResolution = false,