Remove coded uplication in `updateEither`

This commit reduces the code duplication in `updateEither` which was
duplicating a good deal of the resolution logic to deal with the
different resolution mechanisms: the simple one and the cached one.

It also unifies the signatures of the helpers that are invoked by
`updateEither`, removing the weirdness of the different return type
signatures and ad-hoc logic handling.
This commit is contained in:
jvican 2017-04-28 01:12:34 +02:00
parent b68498d013
commit eea500d64f
No known key found for this signature in database
GPG Key ID: 42DAFA0F112E8050
1 changed files with 112 additions and 70 deletions

View File

@ -236,56 +236,32 @@ object IvyActions {
logicalClock: LogicalClock, logicalClock: LogicalClock,
depDir: Option[File], depDir: Option[File],
log: Logger log: Logger
): Either[UnresolvedWarning, UpdateReport] = ): Either[UnresolvedWarning, UpdateReport] = {
module.withModule(log) { module.withModule(log) {
case (ivy, md, default) case (ivy, moduleDescriptor, defaultConf) =>
if module.owner.configuration.updateOptions.cachedResolution && depDir.isDefined => // Warn about duplicated and inconsistent dependencies
ivy.getResolveEngine match { val iw = IvySbt.inconsistentDuplicateWarning(moduleDescriptor)
case x: CachedResolutionResolveEngine => iw.foreach(log.warn(_))
val iw = IvySbt.inconsistentDuplicateWarning(md)
iw foreach { log.warn(_) } // Create inputs, resolve and retrieve the module descriptor
val resolveOptions = new ResolveOptions val inputs = ResolutionInputs(ivy, moduleDescriptor, configuration, log)
val resolveId = ResolveOptions.getDefaultResolveId(md) val resolutionResult: Either[ResolveException, UpdateReport] = {
resolveOptions.setResolveId(resolveId) if (module.owner.configuration.updateOptions.cachedResolution && depDir.isDefined) {
resolveOptions.setArtifactFilter(configuration.artifactFilter) val cache = depDir.getOrElse(sys.error("Missing directory for cached resolution."))
resolveOptions.setLog(ivyLogLevel(configuration.logging)) cachedResolveAndRetrieve(inputs, logicalClock, cache)
x.customResolve( } else resolveAndRetrieve(inputs, defaultConf)
md,
configuration.missingOk,
logicalClock,
resolveOptions,
depDir getOrElse {
sys.error("dependency base directory is not specified")
},
log
) match {
case Left(x) =>
Left(UnresolvedWarning(x, uwconfig))
case Right(uReport) =>
configuration.retrieve match {
case Some(rConf) => Right(retrieve(log, ivy, uReport, rConf))
case None => Right(uReport)
}
}
}
case (ivy, md, default) =>
val iw = IvySbt.inconsistentDuplicateWarning(md)
iw foreach { log.warn(_) }
val (report, err) =
resolve(configuration.logging)(ivy, md, default, configuration.artifactFilter)
err match {
case Some(x) if !configuration.missingOk =>
Left(UnresolvedWarning(x, uwconfig))
case _ =>
val cachedDescriptor = ivy.getSettings.getResolutionCacheManager
.getResolvedIvyFileInCache(md.getModuleRevisionId)
val uReport = IvyRetrieve.updateReport(report, cachedDescriptor)
configuration.retrieve match {
case Some(rConf) => Right(retrieve(log, ivy, uReport, rConf))
case None => Right(uReport)
}
} }
// Convert to unresolved warning or retrieve update report
resolutionResult.fold(
exception => Left(UnresolvedWarning(exception, uwconfig)),
updateReport => {
val retrieveConf = configuration.retrieve
Right(retrieveConf.map(retrieve(log, ivy, updateReport, _)).getOrElse(updateReport))
}
)
} }
}
@deprecated("No longer used.", "0.13.6") @deprecated("No longer used.", "0.13.6")
def processUnresolved(err: ResolveException, log: Logger): Unit = () def processUnresolved(err: ResolveException, log: Logger): Unit = ()
def groupedConflicts[T](moduleFilter: ModuleFilter, grouping: ModuleID => T)( def groupedConflicts[T](moduleFilter: ModuleFilter, grouping: ModuleID => T)(
@ -496,38 +472,104 @@ object IvyActions {
.withConfigurations(if (confs) m.configurations else None) .withConfigurations(if (confs) m.configurations else None)
.branch(m.branchName) .branch(m.branchName)
private[this] def resolve(logging: UpdateLogging)( /**
* Represents the inputs to pass in to [[resolveAndRetrieve]] and [[cachedResolveAndRetrieve]].
*
* @param ivy The ivy instance to resolve and retrieve dependencies.
* @param module The module descriptor to be resolved.
* @param updateConfiguration The update configuration for [[ResolveOptions]].
* @param log The logger.
*/
private case class ResolutionInputs(
ivy: Ivy, ivy: Ivy,
module: DefaultModuleDescriptor, module: DefaultModuleDescriptor,
defaultConf: String, updateConfiguration: UpdateConfiguration,
filter: ArtifactTypeFilter log: Logger
): (ResolveReport, Option[ResolveException]) = { )
/**
* Defines the internal entrypoint of module resolution and retrieval.
*
* This method is the responsible of populating [[ResolveOptions]] and pass
* it in to the ivy instance to perform the module resolution.
*
* It returns an already resolved [[UpdateReport]] instead of a [[ResolveReport]]
* like its counterpart [[CachedResolutionResolveEngine.customResolve]].
*
* @param inputs The resolution inputs.
* @param defaultModuleConfiguration The default ivy configuration.
* @return The result of the resolution.
*/
private[this] def resolveAndRetrieve(
inputs: ResolutionInputs,
defaultModuleConfiguration: String
): Either[ResolveException, UpdateReport] = {
// Populate resolve options from the passed arguments
val ivyInstance = inputs.ivy
val moduleDescriptor = inputs.module
val updateConfiguration = inputs.updateConfiguration
val logging = updateConfiguration.logging
val resolveOptions = new ResolveOptions val resolveOptions = new ResolveOptions
val resolveId = ResolveOptions.getDefaultResolveId(module) val resolveId = ResolveOptions.getDefaultResolveId(moduleDescriptor)
resolveOptions.setResolveId(resolveId) resolveOptions.setResolveId(resolveId)
resolveOptions.setArtifactFilter(filter) resolveOptions.setArtifactFilter(updateConfiguration.artifactFilter)
resolveOptions.setLog(ivyLogLevel(logging)) resolveOptions.setLog(ivyLogLevel(logging))
ResolutionCache.cleanModule( ResolutionCache.cleanModule(
module.getModuleRevisionId, moduleDescriptor.getModuleRevisionId,
resolveId, resolveId,
ivy.getSettings.getResolutionCacheManager ivyInstance.getSettings.getResolutionCacheManager
) )
val resolveReport = ivy.resolve(module, resolveOptions)
val err = val resolveReport = ivyInstance.resolve(moduleDescriptor, resolveOptions)
if (resolveReport.hasError) { if (resolveReport.hasError && !inputs.updateConfiguration.missingOk) {
val messages = resolveReport.getAllProblemMessages.toArray.map(_.toString).distinct // If strict error, collect report information and generated UnresolvedWarning
val failedPaths = Map(resolveReport.getUnresolvedDependencies map { node => val messages = resolveReport.getAllProblemMessages.toArray.map(_.toString).distinct
val m = IvyRetrieve.toModuleID(node.getId) val failedPaths = resolveReport.getUnresolvedDependencies.map { node =>
val path = IvyRetrieve.findPath(node, module.getModuleRevisionId) map { x => val moduleID = IvyRetrieve.toModuleID(node.getId)
IvyRetrieve.toModuleID(x.getId) val path = IvyRetrieve
} .findPath(node, moduleDescriptor.getModuleRevisionId)
m -> path .map(x => IvyRetrieve.toModuleID(x.getId))
}: _*) moduleID -> path
val failed = failedPaths.keys.toSeq }.toMap
Some(new ResolveException(messages, failed, failedPaths)) val failedModules = failedPaths.keys.toSeq
} else None Left(new ResolveException(messages, failedModules, failedPaths))
(resolveReport, err) } else {
// If no strict error, we convert the resolve report into an update report
val cachedDescriptor = ivyInstance.getSettings.getResolutionCacheManager
.getResolvedIvyFileInCache(moduleDescriptor.getModuleRevisionId)
Right(IvyRetrieve.updateReport(resolveReport, cachedDescriptor))
}
} }
/**
* Resolves and retrieves a module with a cache mechanism defined
* <a href="http://www.scala-sbt.org/0.13/docs/Cached-Resolution.html">here</a>.
*
* It's the cached version of [[resolveAndRetrieve]].
*
* @param inputs The resolution inputs.
* @param logicalClock The clock to check if a file is outdated or not.
* @param cache The optional cache dependency.
* @return The result of the cached resolution.
*/
private[this] def cachedResolveAndRetrieve(
inputs: ResolutionInputs,
logicalClock: LogicalClock,
cache: File
): Either[ResolveException, UpdateReport] = {
val log = inputs.log
val descriptor = inputs.module
val updateConfiguration = inputs.updateConfiguration
val resolver = inputs.ivy.getResolveEngine.asInstanceOf[CachedResolutionResolveEngine]
val resolveOptions = new ResolveOptions
val resolveId = ResolveOptions.getDefaultResolveId(descriptor)
resolveOptions.setResolveId(resolveId)
resolveOptions.setArtifactFilter(updateConfiguration.artifactFilter)
resolveOptions.setLog(ivyLogLevel(updateConfiguration.logging))
val acceptError = updateConfiguration.missingOk
resolver.customResolve(descriptor, acceptError, logicalClock, resolveOptions, cache, log)
}
private def retrieve( private def retrieve(
log: Logger, log: Logger,
ivy: Ivy, ivy: Ivy,