mirror of https://github.com/sbt/sbt.git
Allow to print dependency trees
This commit is contained in:
parent
4b0589dc90
commit
3834a9519c
|
|
@ -354,10 +354,16 @@ class Helper(
|
||||||
|
|
||||||
lazy val projCache = res.projectCache.mapValues { case (_, p) => p }
|
lazy val projCache = res.projectCache.mapValues { case (_, p) => p }
|
||||||
|
|
||||||
if (printResultStdout || verbosityLevel >= 1) {
|
if (printResultStdout || verbosityLevel >= 1 || tree || reverseTree) {
|
||||||
if ((printResultStdout && verbosityLevel >= 1) || verbosityLevel >= 2)
|
if ((printResultStdout && verbosityLevel >= 1) || verbosityLevel >= 2 || tree || reverseTree)
|
||||||
errPrintln(s" Result:")
|
errPrintln(s" Result:")
|
||||||
val depsStr = Print.dependenciesUnknownConfigs(trDeps, projCache)
|
|
||||||
|
val depsStr =
|
||||||
|
if (reverseTree || tree)
|
||||||
|
Print.dependencyTree(dependencies, res, printExclusions = verbosityLevel >= 1, reverse = reverseTree)
|
||||||
|
else
|
||||||
|
Print.dependenciesUnknownConfigs(trDeps, projCache)
|
||||||
|
|
||||||
if (printResultStdout)
|
if (printResultStdout)
|
||||||
println(depsStr)
|
println(depsStr)
|
||||||
else
|
else
|
||||||
|
|
|
||||||
|
|
@ -61,6 +61,12 @@ case class CommonOptions(
|
||||||
@Short("B")
|
@Short("B")
|
||||||
@Value("Number of warm-up resolutions - if negative, doesn't print per iteration benchmark (less overhead)")
|
@Value("Number of warm-up resolutions - if negative, doesn't print per iteration benchmark (less overhead)")
|
||||||
benchmark: Int,
|
benchmark: Int,
|
||||||
|
@Help("Print dependencies as a tree")
|
||||||
|
@Short("t")
|
||||||
|
tree: Boolean,
|
||||||
|
@Help("Print dependencies as an inversed tree (dependees as children)")
|
||||||
|
@Short("T")
|
||||||
|
reverseTree: Boolean,
|
||||||
@Recurse
|
@Recurse
|
||||||
cacheOptions: CacheOptions
|
cacheOptions: CacheOptions
|
||||||
) {
|
) {
|
||||||
|
|
|
||||||
|
|
@ -529,6 +529,16 @@ final case class Resolution(
|
||||||
} else
|
} else
|
||||||
Nil
|
Nil
|
||||||
|
|
||||||
|
def dependenciesOf(dep: Dependency, withReconciledVersions: Boolean = true): Seq[Dependency] =
|
||||||
|
if (withReconciledVersions)
|
||||||
|
finalDependencies0(dep).map { trDep =>
|
||||||
|
trDep.copy(
|
||||||
|
version = reconciledVersions.getOrElse(trDep.module, trDep.version)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
else
|
||||||
|
finalDependencies0(dep)
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* Transitive dependencies of the current dependencies, according to
|
* Transitive dependencies of the current dependencies, according to
|
||||||
* what there currently is in cache.
|
* what there currently is in cache.
|
||||||
|
|
@ -558,6 +568,9 @@ final case class Resolution(
|
||||||
forceVersions
|
forceVersions
|
||||||
)
|
)
|
||||||
|
|
||||||
|
def reconciledVersions: Map[Module, String] =
|
||||||
|
nextDependenciesAndConflicts._3
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The modules we miss some info about.
|
* The modules we miss some info about.
|
||||||
*/
|
*/
|
||||||
|
|
@ -974,10 +987,9 @@ final case class Resolution(
|
||||||
* @param dependencies: the dependencies to keep from this `Resolution`
|
* @param dependencies: the dependencies to keep from this `Resolution`
|
||||||
*/
|
*/
|
||||||
def subset(dependencies: Set[Dependency]): Resolution = {
|
def subset(dependencies: Set[Dependency]): Resolution = {
|
||||||
val (_, _, finalVersions) = nextDependenciesAndConflicts
|
|
||||||
|
|
||||||
def updateVersion(dep: Dependency): Dependency =
|
def updateVersion(dep: Dependency): Dependency =
|
||||||
dep.copy(version = finalVersions.getOrElse(dep.module, dep.version))
|
dep.copy(version = reconciledVersions.getOrElse(dep.module, dep.version))
|
||||||
|
|
||||||
@tailrec def helper(current: Set[Dependency]): Set[Dependency] = {
|
@tailrec def helper(current: Set[Dependency]): Set[Dependency] = {
|
||||||
val newDeps = current ++ current
|
val newDeps = current ++ current
|
||||||
|
|
|
||||||
|
|
@ -1,6 +1,6 @@
|
||||||
package coursier.util
|
package coursier.util
|
||||||
|
|
||||||
import coursier.core.{Module, Project, Orders, Dependency}
|
import coursier.core.{ Attributes, Dependency, Module, Orders, Project, Resolution }
|
||||||
|
|
||||||
object Print {
|
object Print {
|
||||||
|
|
||||||
|
|
@ -41,4 +41,154 @@ object Print {
|
||||||
deps1.map(dependency).mkString("\n")
|
deps1.map(dependency).mkString("\n")
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private def compatibleVersions(first: String, second: String): Boolean = {
|
||||||
|
// too loose for now
|
||||||
|
// e.g. RCs and milestones should not be considered compatible with subsequent non-RC or
|
||||||
|
// milestone versions - possibly not with each other either
|
||||||
|
|
||||||
|
first.split('.').take(2).toSeq == second.split('.').take(2).toSeq
|
||||||
|
}
|
||||||
|
|
||||||
|
def dependencyTree(
|
||||||
|
roots: Seq[Dependency],
|
||||||
|
resolution: Resolution,
|
||||||
|
printExclusions: Boolean,
|
||||||
|
reverse: Boolean
|
||||||
|
): String = {
|
||||||
|
|
||||||
|
case class Elem(dep: Dependency, excluded: Boolean) {
|
||||||
|
|
||||||
|
lazy val reconciledVersion = resolution.reconciledVersions
|
||||||
|
.getOrElse(dep.module, dep.version)
|
||||||
|
|
||||||
|
lazy val repr =
|
||||||
|
if (excluded)
|
||||||
|
resolution.reconciledVersions.get(dep.module) match {
|
||||||
|
case None =>
|
||||||
|
s"${Console.YELLOW}(excluded)${Console.RESET} ${dep.module}:${dep.version}"
|
||||||
|
case Some(version) =>
|
||||||
|
val versionMsg =
|
||||||
|
if (version == dep.version)
|
||||||
|
"this version"
|
||||||
|
else
|
||||||
|
s"version $version"
|
||||||
|
|
||||||
|
s"${dep.module}:${dep.version} " +
|
||||||
|
s"${Console.RED}(excluded, $versionMsg present anyway)${Console.RESET}"
|
||||||
|
}
|
||||||
|
else {
|
||||||
|
val versionStr =
|
||||||
|
if (reconciledVersion == dep.version)
|
||||||
|
dep.version
|
||||||
|
else {
|
||||||
|
val assumeCompatibleVersions = compatibleVersions(dep.version, reconciledVersion)
|
||||||
|
|
||||||
|
(if (assumeCompatibleVersions) Console.YELLOW else Console.RED) +
|
||||||
|
s"${dep.version} -> $reconciledVersion" +
|
||||||
|
Console.RESET
|
||||||
|
}
|
||||||
|
|
||||||
|
s"${dep.module}:$versionStr"
|
||||||
|
}
|
||||||
|
|
||||||
|
lazy val children: Seq[Elem] =
|
||||||
|
if (excluded)
|
||||||
|
Nil
|
||||||
|
else {
|
||||||
|
val dep0 = dep.copy(version = reconciledVersion)
|
||||||
|
|
||||||
|
val dependencies = resolution.dependenciesOf(
|
||||||
|
dep0,
|
||||||
|
withReconciledVersions = false
|
||||||
|
).sortBy { trDep =>
|
||||||
|
(trDep.module.organization, trDep.module.name, trDep.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
def excluded = resolution
|
||||||
|
.dependenciesOf(
|
||||||
|
dep0.copy(exclusions = Set.empty),
|
||||||
|
withReconciledVersions = false
|
||||||
|
)
|
||||||
|
.sortBy { trDep =>
|
||||||
|
(trDep.module.organization, trDep.module.name, trDep.version)
|
||||||
|
}
|
||||||
|
.map(_.moduleVersion)
|
||||||
|
.filterNot(dependencies.map(_.moduleVersion).toSet).map {
|
||||||
|
case (mod, ver) =>
|
||||||
|
Elem(
|
||||||
|
Dependency(mod, ver, "", Set.empty, Attributes("", ""), false, false),
|
||||||
|
excluded = true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
dependencies.map(Elem(_, excluded = false)) ++
|
||||||
|
(if (printExclusions) excluded else Nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (reverse) {
|
||||||
|
|
||||||
|
case class Parent(
|
||||||
|
module: Module,
|
||||||
|
version: String,
|
||||||
|
dependsOn: Module,
|
||||||
|
wantVersion: String,
|
||||||
|
gotVersion: String,
|
||||||
|
excluding: Boolean
|
||||||
|
) {
|
||||||
|
lazy val repr: String =
|
||||||
|
if (excluding)
|
||||||
|
s"${Console.YELLOW}(excluded by)${Console.RESET} $module:$version"
|
||||||
|
else if (wantVersion == gotVersion)
|
||||||
|
s"$module:$version"
|
||||||
|
else {
|
||||||
|
val assumeCompatibleVersions = compatibleVersions(wantVersion, gotVersion)
|
||||||
|
|
||||||
|
s"$module:$version " +
|
||||||
|
(if (assumeCompatibleVersions) Console.YELLOW else Console.RED) +
|
||||||
|
s"(wants $dependsOn:$wantVersion, got $gotVersion)" +
|
||||||
|
Console.RESET
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val parents: Map[Module, Seq[Parent]] = {
|
||||||
|
val links = for {
|
||||||
|
dep <- resolution.dependencies.toVector
|
||||||
|
elem <- Elem(dep, excluded = false).children
|
||||||
|
}
|
||||||
|
yield elem.dep.module -> Parent(
|
||||||
|
dep.module,
|
||||||
|
dep.version,
|
||||||
|
elem.dep.module,
|
||||||
|
elem.dep.version,
|
||||||
|
elem.reconciledVersion,
|
||||||
|
elem.excluded
|
||||||
|
)
|
||||||
|
|
||||||
|
links
|
||||||
|
.groupBy(_._1)
|
||||||
|
.mapValues(_.map(_._2).distinct.sortBy(par => (par.module.organization, par.module.name)))
|
||||||
|
.iterator
|
||||||
|
.toMap
|
||||||
|
}
|
||||||
|
|
||||||
|
def children(par: Parent) =
|
||||||
|
if (par.excluding)
|
||||||
|
Nil
|
||||||
|
else
|
||||||
|
parents.getOrElse(par.module, Nil)
|
||||||
|
|
||||||
|
Tree(
|
||||||
|
resolution
|
||||||
|
.dependencies
|
||||||
|
.toVector
|
||||||
|
.sortBy(dep => (dep.module.organization, dep.module.name, dep.version))
|
||||||
|
.map(dep =>
|
||||||
|
Parent(dep.module, dep.version, dep.module, dep.version, dep.version, excluding = false)
|
||||||
|
)
|
||||||
|
)(children, _.repr)
|
||||||
|
} else
|
||||||
|
Tree(roots.toVector.map(Elem(_, excluded = false)))(_.children, _.repr)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -0,0 +1,30 @@
|
||||||
|
package coursier.util
|
||||||
|
|
||||||
|
import scala.collection.mutable.ArrayBuffer
|
||||||
|
|
||||||
|
object Tree {
|
||||||
|
|
||||||
|
def apply[T](roots: IndexedSeq[T])(children: T => Seq[T], print: T => String): String = {
|
||||||
|
|
||||||
|
def helper(elems: Seq[T], prefix: String, acc: String => Unit): Unit =
|
||||||
|
for ((elem, idx) <- elems.zipWithIndex) {
|
||||||
|
val isLast = idx == elems.length - 1
|
||||||
|
|
||||||
|
val tee = if (isLast) "└─ " else "├─ "
|
||||||
|
|
||||||
|
acc(prefix + tee + print(elem))
|
||||||
|
|
||||||
|
val children0 = children(elem)
|
||||||
|
|
||||||
|
if (children0.nonEmpty) {
|
||||||
|
val extraPrefix = if (isLast) " " else "| "
|
||||||
|
helper(children0, prefix + extraPrefix, acc)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
val b = new ArrayBuffer[String]
|
||||||
|
helper(roots, "", b += _)
|
||||||
|
b.mkString("\n")
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|
@ -24,10 +24,26 @@ object CoursierPlugin extends AutoPlugin {
|
||||||
val coursierProjects = Keys.coursierProjects
|
val coursierProjects = Keys.coursierProjects
|
||||||
val coursierPublications = Keys.coursierPublications
|
val coursierPublications = Keys.coursierPublications
|
||||||
val coursierSbtClassifiersModule = Keys.coursierSbtClassifiersModule
|
val coursierSbtClassifiersModule = Keys.coursierSbtClassifiersModule
|
||||||
|
|
||||||
|
val coursierConfigurations = Keys.coursierConfigurations
|
||||||
|
|
||||||
|
val coursierResolution = Keys.coursierResolution
|
||||||
|
val coursierSbtClassifiersResolution = Keys.coursierSbtClassifiersResolution
|
||||||
|
|
||||||
|
val coursierDependencyTree = Keys.coursierDependencyTree
|
||||||
|
val coursierDependencyInverseTree = Keys.coursierDependencyInverseTree
|
||||||
}
|
}
|
||||||
|
|
||||||
import autoImport._
|
import autoImport._
|
||||||
|
|
||||||
|
lazy val treeSettings = Seq(
|
||||||
|
coursierDependencyTree <<= Tasks.coursierDependencyTreeTask(
|
||||||
|
inverse = false
|
||||||
|
),
|
||||||
|
coursierDependencyInverseTree <<= Tasks.coursierDependencyTreeTask(
|
||||||
|
inverse = true
|
||||||
|
)
|
||||||
|
)
|
||||||
|
|
||||||
override lazy val projectSettings = Seq(
|
override lazy val projectSettings = Seq(
|
||||||
coursierParallelDownloads := 6,
|
coursierParallelDownloads := 6,
|
||||||
|
|
@ -53,7 +69,14 @@ object CoursierPlugin extends AutoPlugin {
|
||||||
coursierProject <<= Tasks.coursierProjectTask,
|
coursierProject <<= Tasks.coursierProjectTask,
|
||||||
coursierProjects <<= Tasks.coursierProjectsTask,
|
coursierProjects <<= Tasks.coursierProjectsTask,
|
||||||
coursierPublications <<= Tasks.coursierPublicationsTask,
|
coursierPublications <<= Tasks.coursierPublicationsTask,
|
||||||
coursierSbtClassifiersModule <<= classifiersModule in updateSbtClassifiers
|
coursierSbtClassifiersModule <<= classifiersModule in updateSbtClassifiers,
|
||||||
)
|
coursierConfigurations <<= Tasks.coursierConfigurationsTask,
|
||||||
|
coursierResolution <<= Tasks.resolutionTask(),
|
||||||
|
coursierSbtClassifiersResolution <<= Tasks.resolutionTask(
|
||||||
|
sbtClassifiers = true
|
||||||
|
)
|
||||||
|
) ++
|
||||||
|
inConfig(Compile)(treeSettings) ++
|
||||||
|
inConfig(Test)(treeSettings)
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -27,4 +27,18 @@ object Keys {
|
||||||
val coursierPublications = TaskKey[Seq[(String, Publication)]]("coursier-publications", "")
|
val coursierPublications = TaskKey[Seq[(String, Publication)]]("coursier-publications", "")
|
||||||
|
|
||||||
val coursierSbtClassifiersModule = TaskKey[GetClassifiersModule]("coursier-sbt-classifiers-module", "")
|
val coursierSbtClassifiersModule = TaskKey[GetClassifiersModule]("coursier-sbt-classifiers-module", "")
|
||||||
|
|
||||||
|
val coursierConfigurations = TaskKey[Map[String, Set[String]]]("coursier-configurations", "")
|
||||||
|
|
||||||
|
val coursierResolution = TaskKey[Resolution]("coursier-resolution", "")
|
||||||
|
val coursierSbtClassifiersResolution = TaskKey[Resolution]("coursier-sbt-classifiers-resolution", "")
|
||||||
|
|
||||||
|
val coursierDependencyTree = TaskKey[Unit](
|
||||||
|
"coursier-dependency-tree",
|
||||||
|
"Prints dependencies and transitive dependencies as a tree"
|
||||||
|
)
|
||||||
|
val coursierDependencyInverseTree = TaskKey[Unit](
|
||||||
|
"coursier-dependency-inverse-tree",
|
||||||
|
"Prints dependencies and transitive dependencies as an inverted tree (dependees as children)"
|
||||||
|
)
|
||||||
}
|
}
|
||||||
|
|
|
||||||
|
|
@ -166,16 +166,49 @@ object Tasks {
|
||||||
sbtArtifactsPublication ++ extraSbtArtifactsPublication
|
sbtArtifactsPublication ++ extraSbtArtifactsPublication
|
||||||
}
|
}
|
||||||
|
|
||||||
// FIXME More things should possibly be put here too (resolvers, etc.)
|
def coursierConfigurationsTask = Def.task {
|
||||||
private case class CacheKey(
|
|
||||||
|
val configs0 = ivyConfigurations.value.map { config =>
|
||||||
|
config.name -> config.extendsConfigs.map(_.name)
|
||||||
|
}.toMap
|
||||||
|
|
||||||
|
def allExtends(c: String) = {
|
||||||
|
// possibly bad complexity
|
||||||
|
def helper(current: Set[String]): Set[String] = {
|
||||||
|
val newSet = current ++ current.flatMap(configs0.getOrElse(_, Nil))
|
||||||
|
if ((newSet -- current).nonEmpty)
|
||||||
|
helper(newSet)
|
||||||
|
else
|
||||||
|
newSet
|
||||||
|
}
|
||||||
|
|
||||||
|
helper(Set(c))
|
||||||
|
}
|
||||||
|
|
||||||
|
configs0.map {
|
||||||
|
case (config, _) =>
|
||||||
|
config -> allExtends(config)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private case class ResolutionCacheKey(
|
||||||
project: Project,
|
project: Project,
|
||||||
repositories: Seq[Repository],
|
repositories: Seq[Repository],
|
||||||
resolution: Resolution,
|
resolution: Resolution,
|
||||||
|
sbtClassifiers: Boolean
|
||||||
|
)
|
||||||
|
|
||||||
|
private case class ReportCacheKey(
|
||||||
|
project: Project,
|
||||||
|
resolution: Resolution,
|
||||||
withClassifiers: Boolean,
|
withClassifiers: Boolean,
|
||||||
sbtClassifiers: Boolean
|
sbtClassifiers: Boolean
|
||||||
)
|
)
|
||||||
|
|
||||||
private val resolutionsCache = new mutable.HashMap[CacheKey, UpdateReport]
|
private val resolutionsCache = new mutable.HashMap[ResolutionCacheKey, Resolution]
|
||||||
|
// these may actually not need to be cached any more, now that the resolutions
|
||||||
|
// are cached
|
||||||
|
private val reportsCache = new mutable.HashMap[ReportCacheKey, UpdateReport]
|
||||||
|
|
||||||
private def forcedScalaModules(scalaVersion: String): Map[Module, String] =
|
private def forcedScalaModules(scalaVersion: String): Map[Module, String] =
|
||||||
Map(
|
Map(
|
||||||
|
|
@ -185,17 +218,12 @@ object Tasks {
|
||||||
Module("org.scala-lang", "scalap") -> scalaVersion
|
Module("org.scala-lang", "scalap") -> scalaVersion
|
||||||
)
|
)
|
||||||
|
|
||||||
def updateTask(
|
private def projectDescription(project: Project) =
|
||||||
withClassifiers: Boolean,
|
s"${project.module.organization}:${project.module.name}:${project.version}"
|
||||||
sbtClassifiers: Boolean = false,
|
|
||||||
ignoreArtifactErrors: Boolean = false
|
|
||||||
) = Def.task {
|
|
||||||
|
|
||||||
def grouped[K, V](map: Seq[(K, V)]): Map[K, Seq[V]] =
|
def resolutionTask(
|
||||||
map.groupBy { case (k, _) => k }.map {
|
sbtClassifiers: Boolean = false
|
||||||
case (k, l) =>
|
) = Def.task {
|
||||||
k -> l.map { case (_, v) => v }
|
|
||||||
}
|
|
||||||
|
|
||||||
// let's update only one module at once, for a better output
|
// let's update only one module at once, for a better output
|
||||||
// Downloads are already parallel, no need to parallelize further anyway
|
// Downloads are already parallel, no need to parallelize further anyway
|
||||||
|
|
@ -230,25 +258,10 @@ object Tasks {
|
||||||
(proj.copy(publications = publications), fallbackDeps)
|
(proj.copy(publications = publications), fallbackDeps)
|
||||||
}
|
}
|
||||||
|
|
||||||
val ivySbt0 = ivySbt.value
|
|
||||||
val ivyCacheManager = ivySbt0.withIvy(streams.value.log)(ivy =>
|
|
||||||
ivy.getResolutionCacheManager
|
|
||||||
)
|
|
||||||
|
|
||||||
val ivyModule = ModuleRevisionId.newInstance(
|
|
||||||
currentProject.module.organization,
|
|
||||||
currentProject.module.name,
|
|
||||||
currentProject.version,
|
|
||||||
currentProject.module.attributes.asJava
|
|
||||||
)
|
|
||||||
val cacheIvyFile = ivyCacheManager.getResolvedIvyFileInCache(ivyModule)
|
|
||||||
val cacheIvyPropertiesFile = ivyCacheManager.getResolvedIvyPropertiesInCache(ivyModule)
|
|
||||||
|
|
||||||
val projects = coursierProjects.value
|
val projects = coursierProjects.value
|
||||||
|
|
||||||
val parallelDownloads = coursierParallelDownloads.value
|
val parallelDownloads = coursierParallelDownloads.value
|
||||||
val checksums = coursierChecksums.value
|
val checksums = coursierChecksums.value
|
||||||
val artifactsChecksums = coursierArtifactsChecksums.value
|
|
||||||
val maxIterations = coursierMaxIterations.value
|
val maxIterations = coursierMaxIterations.value
|
||||||
val cachePolicies = coursierCachePolicies.value
|
val cachePolicies = coursierCachePolicies.value
|
||||||
val cache = coursierCache.value
|
val cache = coursierCache.value
|
||||||
|
|
@ -298,22 +311,6 @@ object Tasks {
|
||||||
forceVersions = userForceVersions ++ forcedScalaModules(sv) ++ projects.map(_.moduleVersion)
|
forceVersions = userForceVersions ++ forcedScalaModules(sv) ++ projects.map(_.moduleVersion)
|
||||||
)
|
)
|
||||||
|
|
||||||
// required for publish to be fine, later on
|
|
||||||
def writeIvyFiles() = {
|
|
||||||
val printer = new scala.xml.PrettyPrinter(80, 2)
|
|
||||||
|
|
||||||
val b = new StringBuilder
|
|
||||||
b ++= """<?xml version="1.0" encoding="UTF-8"?>"""
|
|
||||||
b += '\n'
|
|
||||||
b ++= printer.format(MakeIvyXml(currentProject))
|
|
||||||
cacheIvyFile.getParentFile.mkdirs()
|
|
||||||
Files.write(cacheIvyFile.toPath, b.result().getBytes("UTF-8"))
|
|
||||||
|
|
||||||
// Just writing an empty file here... Are these only used?
|
|
||||||
cacheIvyPropertiesFile.getParentFile.mkdirs()
|
|
||||||
Files.write(cacheIvyPropertiesFile.toPath, "".getBytes("UTF-8"))
|
|
||||||
}
|
|
||||||
|
|
||||||
if (verbosityLevel >= 2) {
|
if (verbosityLevel >= 2) {
|
||||||
log.info("InterProjectRepository")
|
log.info("InterProjectRepository")
|
||||||
for (p <- projects)
|
for (p <- projects)
|
||||||
|
|
@ -354,7 +351,7 @@ object Tasks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
def report = {
|
def resolution = {
|
||||||
val pool = Executors.newFixedThreadPool(parallelDownloads, Strategy.DefaultDaemonThreadFactory)
|
val pool = Executors.newFixedThreadPool(parallelDownloads, Strategy.DefaultDaemonThreadFactory)
|
||||||
|
|
||||||
def createLogger() = new TermDisplay(new OutputStreamWriter(System.err))
|
def createLogger() = new TermDisplay(new OutputStreamWriter(System.err))
|
||||||
|
|
@ -374,11 +371,6 @@ object Tasks {
|
||||||
s"${dep.module}:${dep.version}:$config->${dep.configuration}"
|
s"${dep.module}:${dep.version}:$config->${dep.configuration}"
|
||||||
}.sorted.distinct
|
}.sorted.distinct
|
||||||
|
|
||||||
def depsRepr0(deps: Seq[Dependency]) =
|
|
||||||
deps.map { dep =>
|
|
||||||
s"${dep.module}:${dep.version}:${dep.configuration}"
|
|
||||||
}.sorted.distinct
|
|
||||||
|
|
||||||
if (verbosityLevel >= 2) {
|
if (verbosityLevel >= 2) {
|
||||||
val repoReprs = repositories.map {
|
val repoReprs = repositories.map {
|
||||||
case r: IvyRepository =>
|
case r: IvyRepository =>
|
||||||
|
|
@ -399,7 +391,10 @@ object Tasks {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbosityLevel >= 0)
|
if (verbosityLevel >= 0)
|
||||||
log.info(s"Updating ${currentProject.module.organization}:${currentProject.module.name}:${currentProject.version}")
|
log.info(
|
||||||
|
s"Updating ${projectDescription(currentProject)}" +
|
||||||
|
(if (sbtClassifiers) " (sbt classifiers)" else "")
|
||||||
|
)
|
||||||
if (verbosityLevel >= 2)
|
if (verbosityLevel >= 2)
|
||||||
for (depRepr <- depsRepr(currentProject.dependencies))
|
for (depRepr <- depsRepr(currentProject.dependencies))
|
||||||
log.info(s" $depRepr")
|
log.info(s" $depRepr")
|
||||||
|
|
@ -443,34 +438,115 @@ object Tasks {
|
||||||
throw new Exception(s"Encountered ${res.errors.length} error(s) in dependency resolution")
|
throw new Exception(s"Encountered ${res.errors.length} error(s) in dependency resolution")
|
||||||
}
|
}
|
||||||
|
|
||||||
val depsByConfig = grouped(currentProject.dependencies)
|
if (verbosityLevel >= 0)
|
||||||
|
log.info(s"Resolved ${projectDescription(currentProject)} dependencies")
|
||||||
|
|
||||||
val configs = {
|
res
|
||||||
val configs0 = ivyConfigurations.value.map { config =>
|
}
|
||||||
config.name -> config.extendsConfigs.map(_.name)
|
|
||||||
}.toMap
|
|
||||||
|
|
||||||
def allExtends(c: String) = {
|
resolutionsCache.getOrElseUpdate(
|
||||||
// possibly bad complexity
|
ResolutionCacheKey(
|
||||||
def helper(current: Set[String]): Set[String] = {
|
currentProject,
|
||||||
val newSet = current ++ current.flatMap(configs0.getOrElse(_, Nil))
|
repositories,
|
||||||
if ((newSet -- current).nonEmpty)
|
startRes.copy(filter = None),
|
||||||
helper(newSet)
|
sbtClassifiers
|
||||||
else
|
),
|
||||||
newSet
|
resolution
|
||||||
}
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
helper(Set(c))
|
def updateTask(
|
||||||
}
|
withClassifiers: Boolean,
|
||||||
|
sbtClassifiers: Boolean = false,
|
||||||
|
ignoreArtifactErrors: Boolean = false
|
||||||
|
) = Def.task {
|
||||||
|
|
||||||
configs0.map {
|
def grouped[K, V](map: Seq[(K, V)]): Map[K, Seq[V]] =
|
||||||
case (config, _) =>
|
map.groupBy { case (k, _) => k }.map {
|
||||||
config -> allExtends(config)
|
case (k, l) =>
|
||||||
}
|
k -> l.map { case (_, v) => v }
|
||||||
|
}
|
||||||
|
|
||||||
|
// let's update only one module at once, for a better output
|
||||||
|
// Downloads are already parallel, no need to parallelize further anyway
|
||||||
|
synchronized {
|
||||||
|
|
||||||
|
lazy val cm = coursierSbtClassifiersModule.value
|
||||||
|
|
||||||
|
val currentProject =
|
||||||
|
if (sbtClassifiers) {
|
||||||
|
val sv = scalaVersion.value
|
||||||
|
val sbv = scalaBinaryVersion.value
|
||||||
|
|
||||||
|
FromSbt.project(
|
||||||
|
cm.id,
|
||||||
|
cm.modules,
|
||||||
|
cm.configurations.map(cfg => cfg.name -> cfg.extendsConfigs.map(_.name)).toMap,
|
||||||
|
sv,
|
||||||
|
sbv
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val proj = coursierProject.value
|
||||||
|
val publications = coursierPublications.value
|
||||||
|
proj.copy(publications = publications)
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbosityLevel >= 0)
|
val ivySbt0 = ivySbt.value
|
||||||
log.info("Resolution done")
|
val ivyCacheManager = ivySbt0.withIvy(streams.value.log)(ivy =>
|
||||||
|
ivy.getResolutionCacheManager
|
||||||
|
)
|
||||||
|
|
||||||
|
val ivyModule = ModuleRevisionId.newInstance(
|
||||||
|
currentProject.module.organization,
|
||||||
|
currentProject.module.name,
|
||||||
|
currentProject.version,
|
||||||
|
currentProject.module.attributes.asJava
|
||||||
|
)
|
||||||
|
val cacheIvyFile = ivyCacheManager.getResolvedIvyFileInCache(ivyModule)
|
||||||
|
val cacheIvyPropertiesFile = ivyCacheManager.getResolvedIvyPropertiesInCache(ivyModule)
|
||||||
|
|
||||||
|
val parallelDownloads = coursierParallelDownloads.value
|
||||||
|
val artifactsChecksums = coursierArtifactsChecksums.value
|
||||||
|
val cachePolicies = coursierCachePolicies.value
|
||||||
|
val cache = coursierCache.value
|
||||||
|
|
||||||
|
val log = streams.value.log
|
||||||
|
|
||||||
|
val verbosityLevel = coursierVerbosity.value
|
||||||
|
|
||||||
|
// required for publish to be fine, later on
|
||||||
|
def writeIvyFiles() = {
|
||||||
|
val printer = new scala.xml.PrettyPrinter(80, 2)
|
||||||
|
|
||||||
|
val b = new StringBuilder
|
||||||
|
b ++= """<?xml version="1.0" encoding="UTF-8"?>"""
|
||||||
|
b += '\n'
|
||||||
|
b ++= printer.format(MakeIvyXml(currentProject))
|
||||||
|
cacheIvyFile.getParentFile.mkdirs()
|
||||||
|
Files.write(cacheIvyFile.toPath, b.result().getBytes("UTF-8"))
|
||||||
|
|
||||||
|
// Just writing an empty file here... Are these only used?
|
||||||
|
cacheIvyPropertiesFile.getParentFile.mkdirs()
|
||||||
|
Files.write(cacheIvyPropertiesFile.toPath, "".getBytes("UTF-8"))
|
||||||
|
}
|
||||||
|
|
||||||
|
val res = {
|
||||||
|
if (withClassifiers && sbtClassifiers)
|
||||||
|
coursierSbtClassifiersResolution
|
||||||
|
else
|
||||||
|
coursierResolution
|
||||||
|
}.value
|
||||||
|
|
||||||
|
def report = {
|
||||||
|
val pool = Executors.newFixedThreadPool(parallelDownloads, Strategy.DefaultDaemonThreadFactory)
|
||||||
|
|
||||||
|
def createLogger() = new TermDisplay(new OutputStreamWriter(System.err))
|
||||||
|
|
||||||
|
val depsByConfig = grouped(currentProject.dependencies)
|
||||||
|
|
||||||
|
val configs = coursierConfigurations.value
|
||||||
|
|
||||||
if (verbosityLevel >= 2) {
|
if (verbosityLevel >= 2) {
|
||||||
val finalDeps = Config.dependenciesWithConfig(
|
val finalDeps = Config.dependenciesWithConfig(
|
||||||
res,
|
res,
|
||||||
|
|
@ -520,7 +596,10 @@ object Tasks {
|
||||||
}
|
}
|
||||||
|
|
||||||
if (verbosityLevel >= 0)
|
if (verbosityLevel >= 0)
|
||||||
log.info("Fetching artifacts")
|
log.info(
|
||||||
|
s"Fetching artifacts of ${projectDescription(currentProject)}" +
|
||||||
|
(if (sbtClassifiers) " (sbt classifiers)" else "")
|
||||||
|
)
|
||||||
|
|
||||||
artifactsLogger.init()
|
artifactsLogger.init()
|
||||||
|
|
||||||
|
|
@ -534,7 +613,10 @@ object Tasks {
|
||||||
artifactsLogger.stop()
|
artifactsLogger.stop()
|
||||||
|
|
||||||
if (verbosityLevel >= 0)
|
if (verbosityLevel >= 0)
|
||||||
log.info("Fetching artifacts: done")
|
log.info(
|
||||||
|
s"Fetched artifacts of ${projectDescription(currentProject)}" +
|
||||||
|
(if (sbtClassifiers) " (sbt classifiers)" else "")
|
||||||
|
)
|
||||||
|
|
||||||
val artifactFiles = artifactFilesOrErrors.collect {
|
val artifactFiles = artifactFilesOrErrors.collect {
|
||||||
case (artifact, \/-(file)) =>
|
case (artifact, \/-(file)) =>
|
||||||
|
|
@ -602,11 +684,10 @@ object Tasks {
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
resolutionsCache.getOrElseUpdate(
|
reportsCache.getOrElseUpdate(
|
||||||
CacheKey(
|
ReportCacheKey(
|
||||||
currentProject,
|
currentProject,
|
||||||
repositories,
|
res,
|
||||||
startRes.copy(filter = None),
|
|
||||||
withClassifiers,
|
withClassifiers,
|
||||||
sbtClassifiers
|
sbtClassifiers
|
||||||
),
|
),
|
||||||
|
|
@ -615,4 +696,56 @@ object Tasks {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
def coursierDependencyTreeTask(
|
||||||
|
inverse: Boolean,
|
||||||
|
sbtClassifiers: Boolean = false,
|
||||||
|
ignoreArtifactErrors: Boolean = false
|
||||||
|
) = Def.task {
|
||||||
|
|
||||||
|
val currentProject =
|
||||||
|
if (sbtClassifiers) {
|
||||||
|
val cm = coursierSbtClassifiersModule.value
|
||||||
|
val sv = scalaVersion.value
|
||||||
|
val sbv = scalaBinaryVersion.value
|
||||||
|
|
||||||
|
FromSbt.project(
|
||||||
|
cm.id,
|
||||||
|
cm.modules,
|
||||||
|
cm.configurations.map(cfg => cfg.name -> cfg.extendsConfigs.map(_.name)).toMap,
|
||||||
|
sv,
|
||||||
|
sbv
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
val proj = coursierProject.value
|
||||||
|
val publications = coursierPublications.value
|
||||||
|
proj.copy(publications = publications)
|
||||||
|
}
|
||||||
|
|
||||||
|
val res = {
|
||||||
|
if (sbtClassifiers)
|
||||||
|
coursierSbtClassifiersResolution
|
||||||
|
else
|
||||||
|
coursierResolution
|
||||||
|
}.value
|
||||||
|
|
||||||
|
val config = classpathConfiguration.value.name
|
||||||
|
val configs = coursierConfigurations.value
|
||||||
|
|
||||||
|
val includedConfigs = configs.getOrElse(config, Set.empty) + config
|
||||||
|
|
||||||
|
val dependencies0 = currentProject.dependencies.collect {
|
||||||
|
case (cfg, dep) if includedConfigs(cfg) => dep
|
||||||
|
}.sortBy { dep =>
|
||||||
|
(dep.module.organization, dep.module.name, dep.version)
|
||||||
|
}
|
||||||
|
|
||||||
|
val subRes = res.subset(dependencies0.toSet)
|
||||||
|
|
||||||
|
// use sbt logging?
|
||||||
|
println(
|
||||||
|
projectDescription(currentProject) + "\n" +
|
||||||
|
Print.dependencyTree(dependencies0, subRes, printExclusions = true, inverse)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
}
|
}
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue