From 3a3965b5f570d96375e6eba0815f1e90810a7d8d Mon Sep 17 00:00:00 2001 From: Eugene Yokota Date: Wed, 2 Dec 2015 16:41:02 -0500 Subject: [PATCH] Fixes #2264. Use explicit artifacts if any, fallback to hardcoded Even though it's not really used, updateClassifiers constructs dependency graph based on the result from update. The direct cause of #2264 came from the fact that the `allModules` returned from ConfigurationReport did not include dependency configurations. For example it returned "compile" instead of "compile->runtime". I've identified that in #2264 and was fixed by @Duhemm in f49fb33e6d6bc82d86969fbc740341c45a742567. Martin identified that the fix still does not address the fact that updateClassifier hardcodes the classifiers to be tried. This commit adds the fallback behavior so for Ivy-published modules it will use the explicit list of artifacts, and for others it will fallback to the hardcoded list of classifiers. --- ivy/src/main/scala/sbt/IvyActions.scala | 31 +++++++++++++------ ivy/src/main/scala/sbt/UpdateReport.scala | 16 +++++++++- main/src/main/scala/sbt/Defaults.scala | 3 +- ...x-updateclassifiers-configuration.markdown | 13 ++++++++ 4 files changed, 51 insertions(+), 12 deletions(-) create mode 100644 notes/0.13.10/fix-updateclassifiers-configuration.markdown diff --git a/ivy/src/main/scala/sbt/IvyActions.scala b/ivy/src/main/scala/sbt/IvyActions.scala index 419f41a56..657cdd65c 100644 --- a/ivy/src/main/scala/sbt/IvyActions.scala +++ b/ivy/src/main/scala/sbt/IvyActions.scala @@ -231,20 +231,24 @@ object IvyActions { throw w.resolveException } val newConfig = config.copy(module = mod.copy(modules = report.allModules)) - updateClassifiers(ivySbt, newConfig, uwconfig, logicalClock, depDir, log, ???) + updateClassifiers(ivySbt, newConfig, uwconfig, logicalClock, depDir, Vector(), log) } @deprecated("This is no longer public.", "0.13.6") def updateClassifiers(ivySbt: IvySbt, config: GetClassifiersConfiguration, log: Logger): UpdateReport = - updateClassifiers(ivySbt, config, UnresolvedWarningConfiguration(), LogicalClock.unknown, None, log, ???) + updateClassifiers(ivySbt, config, UnresolvedWarningConfiguration(), LogicalClock.unknown, None, Vector(), log) + // artifacts can be obtained from calling toSeq on UpdateReport private[sbt] def updateClassifiers(ivySbt: IvySbt, config: GetClassifiersConfiguration, - uwconfig: UnresolvedWarningConfiguration, logicalClock: LogicalClock, depDir: Option[File], log: Logger, upd: UpdateReport): UpdateReport = + uwconfig: UnresolvedWarningConfiguration, logicalClock: LogicalClock, depDir: Option[File], + artifacts: Vector[(String, ModuleID, Artifact, File)], + log: Logger): UpdateReport = { import config.{ configuration => c, module => mod, _ } import mod.{ configurations => confs, _ } assert(classifiers.nonEmpty, "classifiers cannot be empty") val baseModules = modules map { m => restrictedCopy(m, true) } - val deps = baseModules.distinct flatMap classifiedArtifacts(upd, exclude) //classifiedArtifacts(classifiers, exclude) + // Adding list of explicit artifacts here. + val deps = baseModules.distinct flatMap classifiedArtifacts(classifiers, exclude, artifacts) val base = restrictedCopy(id, true).copy(name = id.name + classifiers.mkString("$", "_", "")) val module = new ivySbt.Module(InlineConfigurationWithExcludes(base, ModuleInfo(base.name), deps).copy(ivyScala = ivyScala, configurations = confs)) val upConf = new UpdateConfiguration(c.retrieve, true, c.logging) @@ -254,14 +258,21 @@ object IvyActions { throw w.resolveException } } - def classifiedArtifacts(updateReport: UpdateReport, exclude: Map[ModuleID, Set[String]])(m: ModuleID): Option[ModuleID] = { + // This version adds explicit artifact + private[sbt] def classifiedArtifacts(classifiers: Seq[String], + exclude: Map[ModuleID, Set[String]], + artifacts: Vector[(String, ModuleID, Artifact, File)])(m: ModuleID): Option[ModuleID] = { def sameModule(m1: ModuleID, m2: ModuleID): Boolean = m1.organization == m2.organization && m1.name == m2.name && m1.revision == m2.revision - updateReport.toSeq.groupBy(_._2) collectFirst { - case (k, v) if sameModule(k, m) => - val arts = v.collect { case (_, _, art, _) if art.classifier.isDefined => art }.distinct - m.copy(isTransitive = false, explicitArtifacts = arts) - } + def explicitArtifacts = + { + val arts = (artifacts collect { case (_, x, art, _) if sameModule(m, x) && art.classifier.isDefined => art }).distinct + if (arts.isEmpty) None + else Some(m.copy(isTransitive = false, explicitArtifacts = arts)) + } + def hardcodedArtifacts = classifiedArtifacts(classifiers, exclude)(m) + explicitArtifacts orElse hardcodedArtifacts } + @deprecated("This is no longer public.", "0.13.10") def classifiedArtifacts(classifiers: Seq[String], exclude: Map[ModuleID, Set[String]])(m: ModuleID): Option[ModuleID] = { val excluded = exclude getOrElse (restrictedCopy(m, false), Set.empty) diff --git a/ivy/src/main/scala/sbt/UpdateReport.scala b/ivy/src/main/scala/sbt/UpdateReport.scala index d7d364b47..6cb401c5f 100644 --- a/ivy/src/main/scala/sbt/UpdateReport.scala +++ b/ivy/src/main/scala/sbt/UpdateReport.scala @@ -200,7 +200,21 @@ final class UpdateReport(val cachedDescriptor: File, val configurations: Seq[Con override def toString = "Update report:\n\t" + stats + "\n" + configurations.mkString /** All resolved modules in all configurations. */ - def allModules: Seq[ModuleID] = configurations.flatMap(_.allModules).distinct + def allModules: Seq[ModuleID] = + { + val key = (m: ModuleID) => (m.organization, m.name, m.revision) + configurations.flatMap(_.allModules).groupBy(key).toSeq map { + case (k, v) => + v reduceLeft { (agg, x) => + agg.copy( + configurations = (agg.configurations, x.configurations) match { + case (None, _) => x.configurations + case (Some(ac), None) => Some(ac) + case (Some(ac), Some(xc)) => Some(s"$ac;$xc") + }) + } + } + } def retrieve(f: (String, ModuleID, Artifact, File) => File): UpdateReport = new UpdateReport(cachedDescriptor, configurations map { _ retrieve f }, stats, stamps) diff --git a/main/src/main/scala/sbt/Defaults.scala b/main/src/main/scala/sbt/Defaults.scala index 24baf2d34..55730060c 100755 --- a/main/src/main/scala/sbt/Defaults.scala +++ b/main/src/main/scala/sbt/Defaults.scala @@ -1234,7 +1234,8 @@ object Classpaths { val uwConfig = (unresolvedWarningConfiguration in update).value val logicalClock = LogicalClock(state.value.hashCode) val depDir = dependencyCacheDirectory.value - IvyActions.updateClassifiers(is, GetClassifiersConfiguration(mod, excludes, c, ivyScala.value), uwConfig, LogicalClock(state.value.hashCode), Some(depDir), s.log, update.value) + val artifacts = update.value.toSeq.toVector + IvyActions.updateClassifiers(is, GetClassifiersConfiguration(mod, excludes, c, ivyScala.value), uwConfig, LogicalClock(state.value.hashCode), Some(depDir), artifacts, s.log) } } tag (Tags.Update, Tags.Network) ) diff --git a/notes/0.13.10/fix-updateclassifiers-configuration.markdown b/notes/0.13.10/fix-updateclassifiers-configuration.markdown new file mode 100644 index 000000000..b69053d5a --- /dev/null +++ b/notes/0.13.10/fix-updateclassifiers-configuration.markdown @@ -0,0 +1,13 @@ + + [@eed3si9n]: https://github.com/eed3si9n + [@Duhemm]: https://github.com/Duhemm + [2264]: https://github.com/sbt/sbt/issues/2264 + +### Fixes with compatibility implications + +### Improvements + +### Bug fixes + +- Fixes `updateClassifiers` on Ivy modules without `default` configuration. + [#2264][2264] by [@eed3si9n][@eed3si9n]/[@Duhemm][@Duhemm]