Fixes #1827. Adds withInterProjectFirst to update option.

withInterProjectFirst when set to true will prioritize inter-project
resolver over all other resolver and Ivy cache.
This aimed to workaround the fact that on Maven Test configuration is
considered private, and thus project dependency with `test->test`
configuration may not under some condition. The condition is when
someone resolves `x:y:1.0` and you have a subproject named and
versioned exactly that, and some other subproject tries to depend on
it. This happens when the project does not change the version number on
the Github.
This commit is contained in:
Eugene Yokota 2015-12-31 01:22:27 -05:00 committed by Dale Wijnand
parent b705d4e998
commit eef906e09a
2 changed files with 66 additions and 49 deletions

View File

@ -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
}
}
}

View File

@ -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,