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 }
|
||||
|
||||
if (printResultStdout || verbosityLevel >= 1) {
|
||||
if ((printResultStdout && verbosityLevel >= 1) || verbosityLevel >= 2)
|
||||
if (printResultStdout || verbosityLevel >= 1 || tree || reverseTree) {
|
||||
if ((printResultStdout && verbosityLevel >= 1) || verbosityLevel >= 2 || tree || reverseTree)
|
||||
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)
|
||||
println(depsStr)
|
||||
else
|
||||
|
|
|
|||
|
|
@ -61,6 +61,12 @@ case class CommonOptions(
|
|||
@Short("B")
|
||||
@Value("Number of warm-up resolutions - if negative, doesn't print per iteration benchmark (less overhead)")
|
||||
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
|
||||
cacheOptions: CacheOptions
|
||||
) {
|
||||
|
|
|
|||
|
|
@ -529,6 +529,16 @@ final case class Resolution(
|
|||
} else
|
||||
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
|
||||
* what there currently is in cache.
|
||||
|
|
@ -558,6 +568,9 @@ final case class Resolution(
|
|||
forceVersions
|
||||
)
|
||||
|
||||
def reconciledVersions: Map[Module, String] =
|
||||
nextDependenciesAndConflicts._3
|
||||
|
||||
/**
|
||||
* The modules we miss some info about.
|
||||
*/
|
||||
|
|
@ -974,10 +987,9 @@ final case class Resolution(
|
|||
* @param dependencies: the dependencies to keep from this `Resolution`
|
||||
*/
|
||||
def subset(dependencies: Set[Dependency]): Resolution = {
|
||||
val (_, _, finalVersions) = nextDependenciesAndConflicts
|
||||
|
||||
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] = {
|
||||
val newDeps = current ++ current
|
||||
|
|
|
|||
|
|
@ -1,6 +1,6 @@
|
|||
package coursier.util
|
||||
|
||||
import coursier.core.{Module, Project, Orders, Dependency}
|
||||
import coursier.core.{ Attributes, Dependency, Module, Orders, Project, Resolution }
|
||||
|
||||
object Print {
|
||||
|
||||
|
|
@ -41,4 +41,154 @@ object Print {
|
|||
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 coursierPublications = Keys.coursierPublications
|
||||
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._
|
||||
|
||||
lazy val treeSettings = Seq(
|
||||
coursierDependencyTree <<= Tasks.coursierDependencyTreeTask(
|
||||
inverse = false
|
||||
),
|
||||
coursierDependencyInverseTree <<= Tasks.coursierDependencyTreeTask(
|
||||
inverse = true
|
||||
)
|
||||
)
|
||||
|
||||
override lazy val projectSettings = Seq(
|
||||
coursierParallelDownloads := 6,
|
||||
|
|
@ -53,7 +69,14 @@ object CoursierPlugin extends AutoPlugin {
|
|||
coursierProject <<= Tasks.coursierProjectTask,
|
||||
coursierProjects <<= Tasks.coursierProjectsTask,
|
||||
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 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
|
||||
}
|
||||
|
||||
// FIXME More things should possibly be put here too (resolvers, etc.)
|
||||
private case class CacheKey(
|
||||
def coursierConfigurationsTask = Def.task {
|
||||
|
||||
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,
|
||||
repositories: Seq[Repository],
|
||||
resolution: Resolution,
|
||||
sbtClassifiers: Boolean
|
||||
)
|
||||
|
||||
private case class ReportCacheKey(
|
||||
project: Project,
|
||||
resolution: Resolution,
|
||||
withClassifiers: 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] =
|
||||
Map(
|
||||
|
|
@ -185,17 +218,12 @@ object Tasks {
|
|||
Module("org.scala-lang", "scalap") -> scalaVersion
|
||||
)
|
||||
|
||||
def updateTask(
|
||||
withClassifiers: Boolean,
|
||||
sbtClassifiers: Boolean = false,
|
||||
ignoreArtifactErrors: Boolean = false
|
||||
) = Def.task {
|
||||
private def projectDescription(project: Project) =
|
||||
s"${project.module.organization}:${project.module.name}:${project.version}"
|
||||
|
||||
def grouped[K, V](map: Seq[(K, V)]): Map[K, Seq[V]] =
|
||||
map.groupBy { case (k, _) => k }.map {
|
||||
case (k, l) =>
|
||||
k -> l.map { case (_, v) => v }
|
||||
}
|
||||
def resolutionTask(
|
||||
sbtClassifiers: Boolean = false
|
||||
) = Def.task {
|
||||
|
||||
// let's update only one module at once, for a better output
|
||||
// Downloads are already parallel, no need to parallelize further anyway
|
||||
|
|
@ -230,25 +258,10 @@ object Tasks {
|
|||
(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 parallelDownloads = coursierParallelDownloads.value
|
||||
val checksums = coursierChecksums.value
|
||||
val artifactsChecksums = coursierArtifactsChecksums.value
|
||||
val maxIterations = coursierMaxIterations.value
|
||||
val cachePolicies = coursierCachePolicies.value
|
||||
val cache = coursierCache.value
|
||||
|
|
@ -298,22 +311,6 @@ object Tasks {
|
|||
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) {
|
||||
log.info("InterProjectRepository")
|
||||
for (p <- projects)
|
||||
|
|
@ -354,7 +351,7 @@ object Tasks {
|
|||
}
|
||||
}
|
||||
|
||||
def report = {
|
||||
def resolution = {
|
||||
val pool = Executors.newFixedThreadPool(parallelDownloads, Strategy.DefaultDaemonThreadFactory)
|
||||
|
||||
def createLogger() = new TermDisplay(new OutputStreamWriter(System.err))
|
||||
|
|
@ -374,11 +371,6 @@ object Tasks {
|
|||
s"${dep.module}:${dep.version}:$config->${dep.configuration}"
|
||||
}.sorted.distinct
|
||||
|
||||
def depsRepr0(deps: Seq[Dependency]) =
|
||||
deps.map { dep =>
|
||||
s"${dep.module}:${dep.version}:${dep.configuration}"
|
||||
}.sorted.distinct
|
||||
|
||||
if (verbosityLevel >= 2) {
|
||||
val repoReprs = repositories.map {
|
||||
case r: IvyRepository =>
|
||||
|
|
@ -399,7 +391,10 @@ object Tasks {
|
|||
}
|
||||
|
||||
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)
|
||||
for (depRepr <- depsRepr(currentProject.dependencies))
|
||||
log.info(s" $depRepr")
|
||||
|
|
@ -443,34 +438,115 @@ object Tasks {
|
|||
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 = {
|
||||
val configs0 = ivyConfigurations.value.map { config =>
|
||||
config.name -> config.extendsConfigs.map(_.name)
|
||||
}.toMap
|
||||
res
|
||||
}
|
||||
|
||||
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
|
||||
}
|
||||
resolutionsCache.getOrElseUpdate(
|
||||
ResolutionCacheKey(
|
||||
currentProject,
|
||||
repositories,
|
||||
startRes.copy(filter = None),
|
||||
sbtClassifiers
|
||||
),
|
||||
resolution
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
helper(Set(c))
|
||||
}
|
||||
def updateTask(
|
||||
withClassifiers: Boolean,
|
||||
sbtClassifiers: Boolean = false,
|
||||
ignoreArtifactErrors: Boolean = false
|
||||
) = Def.task {
|
||||
|
||||
configs0.map {
|
||||
case (config, _) =>
|
||||
config -> allExtends(config)
|
||||
}
|
||||
def grouped[K, V](map: Seq[(K, V)]): Map[K, Seq[V]] =
|
||||
map.groupBy { case (k, _) => k }.map {
|
||||
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)
|
||||
log.info("Resolution done")
|
||||
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 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) {
|
||||
val finalDeps = Config.dependenciesWithConfig(
|
||||
res,
|
||||
|
|
@ -520,7 +596,10 @@ object Tasks {
|
|||
}
|
||||
|
||||
if (verbosityLevel >= 0)
|
||||
log.info("Fetching artifacts")
|
||||
log.info(
|
||||
s"Fetching artifacts of ${projectDescription(currentProject)}" +
|
||||
(if (sbtClassifiers) " (sbt classifiers)" else "")
|
||||
)
|
||||
|
||||
artifactsLogger.init()
|
||||
|
||||
|
|
@ -534,7 +613,10 @@ object Tasks {
|
|||
artifactsLogger.stop()
|
||||
|
||||
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 {
|
||||
case (artifact, \/-(file)) =>
|
||||
|
|
@ -602,11 +684,10 @@ object Tasks {
|
|||
)
|
||||
}
|
||||
|
||||
resolutionsCache.getOrElseUpdate(
|
||||
CacheKey(
|
||||
reportsCache.getOrElseUpdate(
|
||||
ReportCacheKey(
|
||||
currentProject,
|
||||
repositories,
|
||||
startRes.copy(filter = None),
|
||||
res,
|
||||
withClassifiers,
|
||||
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