All Downloads are FREE. Search and download functionalities are using the official Maven repository.

scala.coursier.graph.ReverseModuleTree.scala Maven / Gradle / Ivy

There is a newer version: 2.1.13
Show newest version
package coursier.graph

import coursier.core.{Module, Resolution}

import scala.collection.compat._
import scala.collection.mutable

/** Tree allowing to walk the dependency graph from dependency to dependees. */
sealed abstract class ReverseModuleTree {
  def module: Module

  /** The final version of this dependency. */
  def reconciledVersion: String

  // some info about what we depend on, our "parent" in this inverse tree

  /** Module of the parent dependency of to this node.
    *
    * This node is a dependee. This method corresponds to what we depend on.
    */
  def dependsOnModule: Module

  /** Version of the parent dependency of to this node.
    *
    * This node is a dependee. This method corresponds to what we depend on.
    *
    * This is the version this module explicitly depends on.
    */
  def dependsOnVersion: String

  /** Final version of the parent dependency of to this node.
    *
    * This node is a dependee. This method corresponds to what we depend on.
    *
    * This is the version that was selected during resolution.
    */
  def dependsOnReconciledVersion: String

  /** Whether the parent dependency was excluded by us, but landed anyway in the classpath.
    *
    * This node is a dependee. This method corresponds to what we depend on.
    *
    * This says whether this dependency was excluded by us, but landed anyway in the classpath.
    */
  def excludedDependsOn: Boolean

  /** Dependees of this module. */
  def dependees: Seq[ReverseModuleTree]
}

object ReverseModuleTree {

  def fromModuleTree(roots: Seq[Module], moduleTrees: Seq[ModuleTree]): Seq[ReverseModuleTree] = {

    // Some assumptions about moduleTrees, and the ModuleTree-s we get though their children:
    // - any two ModuleTree-s with the same module are assumed to have the same reconciledVersion
    // - if recursing on children leads to cycles, it is assumed we'll cycle through ModuleTree instances that are ==
    //   (that is, keeping a set of the already seen ModuleTree should be enough to detect those cycles).

    // Some *non*-assumptions:
    // - ModuleTree-s with the same module have the same children: this doesn't necessarily hold, as two ModuleTree-s
    //   may originate from two different dependencies (with different exclusions for example), bringing possibly
    //   different children dependencies.

    val alreadySeen = new mutable.HashSet[ModuleTree]
    val dependees   = new mutable.HashMap[Module, mutable.HashSet[(Module, String, Boolean)]]
    val versions    = new mutable.HashMap[Module, (String, String)]
    val toCheck     = new mutable.Queue[ModuleTree]

    toCheck ++= moduleTrees

    while (toCheck.nonEmpty) {
      val elem = toCheck.dequeue()
      alreadySeen += elem
      versions.put(elem.module, (elem.reconciledVersion, elem.retainedVersion))
      val children = elem.children
      toCheck ++= children.filterNot(alreadySeen)
      for (c <- children) {
        val b = dependees.getOrElseUpdate(c.module, new mutable.HashSet[(Module, String, Boolean)])
        b.add((elem.module, c.retainedVersion, false))
      }
    }

    val dependees0 = dependees
      .toMap
      .view
      .mapValues(_.toVector.sortBy(t =>
        (t._1.organization.value, t._1.name.value, t._1.nameWithAttributes)
      ))
      .iterator
      .toMap
    val versions0 = versions.toMap

    for {
      m                      <- roots
      (reconciled, retained) <- versions.get(m)
    } yield Node(
      m,
      reconciled,
      retained,
      m,
      reconciled,
      reconciled,
      excludedDependsOn = false,
      dependees0,
      versions0
    )
  }

  def fromDependencyTree(
    roots: Seq[Module],
    dependencyTrees: Seq[DependencyTree]
  ): Seq[ReverseModuleTree] = {

    val alreadySeen = new mutable.HashSet[DependencyTree]
    val dependees   = new mutable.HashMap[Module, mutable.HashSet[(Module, String, Boolean)]]
    val versions    = new mutable.HashMap[Module, (String, String)]
    val toCheck     = new mutable.Queue[DependencyTree]

    toCheck ++= dependencyTrees

    while (toCheck.nonEmpty) {
      val elem = toCheck.dequeue()
      alreadySeen += elem
      versions.put(elem.dependency.module, (elem.reconciledVersion, elem.retainedVersion))
      val children = elem.children
      toCheck ++= children.filterNot(alreadySeen)
      for (c <- children) {
        val b = dependees.getOrElseUpdate(
          c.dependency.module,
          new mutable.HashSet[(Module, String, Boolean)]
        )
        b.add((elem.dependency.module, c.dependency.version, c.excluded))
      }
    }

    val dependees0 = dependees
      .toMap
      .view
      .mapValues(_.toVector.sortBy(t =>
        (t._1.organization.value, t._1.name.value, t._1.nameWithAttributes)
      ))
      .iterator
      .toMap
    val versions0 = versions.toMap

    for {
      m                      <- roots
      (reconciled, retained) <- versions.get(m)
    } yield Node(
      m,
      reconciled,
      retained,
      m,
      reconciled,
      reconciled,
      excludedDependsOn = false,
      dependees0,
      versions0
    )
  }

  def apply(
    resolution: Resolution,
    roots: Seq[Module] = null,
    withExclusions: Boolean = false
  ): Seq[ReverseModuleTree] = {
    val t      = DependencyTree(resolution, withExclusions = withExclusions)
    val roots0 = Option(roots).getOrElse(resolution.minDependencies.toVector.map(_.module))
    fromDependencyTree(roots0, t)
  }

  private[graph] final case class Node(
    module: Module,
    reconciledVersion: String,
    retainedVersion: String,
    dependsOnModule: Module,
    dependsOnVersion: String,
    dependsOnReconciledVersion: String,
    excludedDependsOn: Boolean,
    allDependees: Map[Module, Seq[(Module, String, Boolean)]],
    versions: Map[Module, (String, String)]
  ) extends ReverseModuleTree {
    def dependees: Seq[Node] =
      for {
        (m, wantVer, excl)     <- allDependees.getOrElse(module, Nil)
        (reconciled, retained) <- versions.get(m)
      } yield Node(
        m,
        reconciled,
        retained,
        module,
        wantVer,
        reconciledVersion,
        excl,
        allDependees,
        versions
      )
  }

}




© 2015 - 2024 Weber Informatics LLC | Privacy Policy