diff --git a/modules/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala b/modules/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala index b3fbea90b..94ebf893e 100644 --- a/modules/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala +++ b/modules/lm-coursier/src/main/scala/lmcoursier/CoursierDependencyResolution.scala @@ -210,6 +210,7 @@ class CoursierDependencyResolution(conf: CoursierConfiguration) extends Dependen classifiers = classifiers, configs = configs, dependencies = dependencies, + forceVersions = conf.forceVersions.map { case (m, v) => (ToCoursier.module(m), v) }.toMap, interProjectDependencies = interProjectDependencies, res = resolutions, includeSignatures = false, diff --git a/modules/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala b/modules/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala index 11d7d67ae..26e552030 100644 --- a/modules/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala +++ b/modules/lm-coursier/src/main/scala/lmcoursier/internal/SbtUpdateReport.scala @@ -296,7 +296,8 @@ private[internal] object SbtUpdateReport { log: Logger, includeSignatures: Boolean, classpathOrder: Boolean, - missingOk: Boolean + missingOk: Boolean, + forceVersions: Map[Module, String] ): UpdateReport = { val configReports = resolutions.map { @@ -334,27 +335,30 @@ private[internal] object SbtUpdateReport { OrganizationArtifactReport(rep.module.organization, rep.module.name, Vector(rep)) } - val evicted = coursier.graph.Conflict(subRes).flatMap { c => - // FIXME The project for c.wantedVersion is possibly not around (it's likely it was just not fetched) - val projOpt = subRes.projectCache.get((c.module, c.wantedVersion)) + val evicted = for { + c <- coursier.graph.Conflict(subRes) + // ideally, forceVersions should be taken into account by coursier.core.Resolution itself, when + // it computes transitive dependencies. It only handles forced versions at a global level for now, + // rather than handing them for each dependency (where each dependency could have its own forced + // versions, and apply and pass them to its transitive dependencies, just like for exclusions today). + if !forceVersions.contains(c.module) + projOpt = subRes.projectCache.get((c.module, c.wantedVersion)) .orElse(subRes.projectCache.get((c.module, c.version))) - projOpt.toSeq.map { - case (_, proj) => - // likely misses some details (transitive, exclusions, …) - val dep = Dependency(c.module, c.wantedVersion) - val dependee = Dependency(c.dependeeModule, c.dependeeVersion) - val dependeeProj = subRes.projectCache.get((c.dependeeModule, c.dependeeVersion)) match { - case Some((_, p)) => - ProjectInfo(p.version, p.configurations.keys.toVector.map(c => ConfigRef(c.value)), p.properties) - case None => - // should not happen - ProjectInfo(c.dependeeVersion, Vector.empty, Vector.empty) - } - val rep = moduleReport((dep, Seq((dependee, dependeeProj)), proj.withVersion(c.wantedVersion), Nil)) - .withEvicted(true) - .withEvictedData(Some("version selection")) // ??? put latest-revision like sbt/ivy here? - OrganizationArtifactReport(c.module.organization.value, c.module.name.value, Vector(rep)) - } + (_, proj) <- projOpt.toSeq + } yield { + val dep = Dependency(c.module, c.wantedVersion) + val dependee = Dependency(c.dependeeModule, c.dependeeVersion) + val dependeeProj = subRes.projectCache.get((c.dependeeModule, c.dependeeVersion)) match { + case Some((_, p)) => + ProjectInfo(p.version, p.configurations.keys.toVector.map(c => ConfigRef(c.value)), p.properties) + case None => + // should not happen + ProjectInfo(c.dependeeVersion, Vector.empty, Vector.empty) + } + val rep = moduleReport((dep, Seq((dependee, dependeeProj)), proj.withVersion(c.wantedVersion), Nil)) + .withEvicted(true) + .withEvictedData(Some("version selection")) // ??? put latest-revision like sbt/ivy here? + OrganizationArtifactReport(c.module.organization.value, c.module.name.value, Vector(rep)) } val details = (mainReportDetails ++ evicted) diff --git a/modules/lm-coursier/src/main/scala/lmcoursier/internal/UpdateParams.scala b/modules/lm-coursier/src/main/scala/lmcoursier/internal/UpdateParams.scala index 8f23a70c0..3de9f73fd 100644 --- a/modules/lm-coursier/src/main/scala/lmcoursier/internal/UpdateParams.scala +++ b/modules/lm-coursier/src/main/scala/lmcoursier/internal/UpdateParams.scala @@ -13,6 +13,7 @@ final case class UpdateParams( classifiers: Option[Seq[Classifier]], configs: Map[Configuration, Set[Configuration]], dependencies: Seq[(Configuration, Dependency)], + forceVersions: Map[Module, String], interProjectDependencies: Seq[Project], res: Map[Configuration, Resolution], includeSignatures: Boolean, diff --git a/modules/lm-coursier/src/main/scala/lmcoursier/internal/UpdateRun.scala b/modules/lm-coursier/src/main/scala/lmcoursier/internal/UpdateRun.scala index 8cf470894..6b5fcb571 100644 --- a/modules/lm-coursier/src/main/scala/lmcoursier/internal/UpdateRun.scala +++ b/modules/lm-coursier/src/main/scala/lmcoursier/internal/UpdateRun.scala @@ -82,7 +82,8 @@ object UpdateRun { log, includeSignatures = params.includeSignatures, classpathOrder = params.classpathOrder, - missingOk = params.missingOk + missingOk = params.missingOk, + params.forceVersions ) } diff --git a/modules/sbt-coursier/src/main/scala/coursier/sbtcoursier/UpdateTasks.scala b/modules/sbt-coursier/src/main/scala/coursier/sbtcoursier/UpdateTasks.scala index 53b57b000..2429297a7 100644 --- a/modules/sbt-coursier/src/main/scala/coursier/sbtcoursier/UpdateTasks.scala +++ b/modules/sbt-coursier/src/main/scala/coursier/sbtcoursier/UpdateTasks.scala @@ -1,10 +1,11 @@ package coursier.sbtcoursier import coursier.core._ -import lmcoursier.definitions.ToCoursier -import lmcoursier.internal.{SbtBootJars, SbtCoursierCache, UpdateParams, UpdateRun} import coursier.sbtcoursier.Keys._ import coursier.sbtcoursiershared.SbtCoursierShared.autoImport._ +import lmcoursier.definitions.ToCoursier +import lmcoursier.Inputs +import lmcoursier.internal.{SbtBootJars, SbtCoursierCache, UpdateParams, UpdateRun} import sbt.Def import sbt.Keys._ import sbt.librarymanagement.UpdateReport @@ -110,6 +111,14 @@ object UpdateTasks { val artifactFilesOrErrors0 = artifactFilesOrErrors0Task.value val classifiers = classifiersTask.value val configs = configsTask.value + val sv = scalaVersion.value + val sbv = scalaBinaryVersion.value + val forceVersions = Inputs.forceVersions(dependencyOverrides.value, sv, sbv) + .map { + case (m, v) => + (ToCoursier.module(m), v) + } + .toMap val params = UpdateParams( (p.module, p.version), @@ -118,6 +127,7 @@ object UpdateTasks { classifiers, configs, dependencies, + forceVersions, interProjectDependencies, res, includeSignatures, diff --git a/modules/sbt-coursier/src/sbt-test/sbt-lm-coursier/evicted/build.sbt b/modules/sbt-coursier/src/sbt-test/sbt-lm-coursier/evicted/build.sbt index e9009dee7..cf0b4ab9b 100644 --- a/modules/sbt-coursier/src/sbt-test/sbt-lm-coursier/evicted/build.sbt +++ b/modules/sbt-coursier/src/sbt-test/sbt-lm-coursier/evicted/build.sbt @@ -19,14 +19,25 @@ lazy val b = project ) ) +lazy val c = project + .settings( + scalaVersion := "2.12.8", + libraryDependencies ++= Seq( + "org.slf4s" %% "slf4s-api" % "1.7.25", // depends on org.slf4j:slf4j-api:1.7.25 + "ch.qos.logback" % "logback-classic" % "1.1.2" // depends on org.slf4j:slf4j-api:1.7.6 + ), + dependencyOverrides += "org.slf4j" % "slf4j-api" % "1.7.30" +) + lazy val check = taskKey[Unit]("") check := { val aReport = update.in(a).value val bReport = update.in(b).value + val cReport = update.in(c).value - def doCheck(report: UpdateReport): Unit = { + def doCheck(report: UpdateReport, evictionsExpected: Boolean = true): Unit = { val compileReport = report .configurations @@ -36,11 +47,12 @@ check := { } val foundEvictions = compileReport.details.exists(_.modules.exists(_.evicted)) - if (!foundEvictions) + if (foundEvictions != evictionsExpected) compileReport.details.foreach(println) - assert(foundEvictions) + assert(foundEvictions == evictionsExpected) } doCheck(aReport) doCheck(bReport) + doCheck(cReport, evictionsExpected = false) }