查看 sbt 项目的依赖关系树

sbt 是借助于 ivy 来管理项目依赖, 像 Maven 项目中可以用 dependency:tree 来显示依赖树, 那么对于 sbt 项目该如何查看项目依赖关系呢? 本文提及了三种方式来显示项目依赖, 它们是 Shell 脚本, 自定义 sbt 任务, 和 sbt-dependency-plugin 方式. 最后一个办法使得我们也能用 dependencyTree 显示出 Maven 的  dependency:tree 效果来, 还有更酷的的.

> dependencyTree
[info] default:test_2.10:0.1-SNAPSHOT [S]
[info]   +-ch.qos.logback:logback-classic:1.0.13
[info]     +-ch.qos.logback:logback-core:1.0.13
[info]     +-org.slf4j:slf4j-api:1.7.5
[info]
[success] Total time: 0 s, completed Apr 5, 2016 12:29:53 AM

下面是探索的全部过程.

通过 sbt 控制台的 tab  自动完成或用 help .*[Dd]ependenc.* 命令再进一步过滤出与依赖比较接近 sbt 控制台任务

dependencyClasspath  The classpath consisting of internal and external, managed and unmanaged dependencies.
externalDependencyClasspath  The classpath consisting of library dependencies, both managed and unmanaged.
internalDependencyClasspath  The internal (inter-project) classpath.

projectDependencies  Inter-project dependencies.
excludeDependencies  Declares managed dependency exclusions.
dependencyCacheDirectory  The base directory for cached dependencies.
allDependencies  Inter-project and library dependencies.
libraryDependencies  Declares managed dependencies.
dependencyOverrides  Declares managed dependency overrides.
trackInternalDependencies  The level of tracking for the internal (inter-project) dependency.
dependencyPositions  Source positions where the dependencies are defined.

来深入, 我们建立一个简单的项目, 文件目录如下

test
├── build.sbt
└── lib
    └── guava-18.0.jar

build.sbt 文件的内容是

libraryDependencies ++= Seq(
  "ch.qos.logback" % "logback-classic" % "1.0.13"
)

我们来看看 sbt 的  dependencyClasspath, externalDependencyClasspath, 和 internalDependencyClasspath 的输出

> show dependencyClasspath
[info] List(Attributed(/Users/uqiu/test/lib/guava-18.0.jar), Attributed(/Users/uqiu/.sbt/boot/scala-2.10.6/lib/scala-library.jar), Attributed(/Users/uqiu/.ivy2/cache/ch.qos.logback/logback-classic/jars/logback-classic-1.0.13.jar), Attributed(/Users/uqiu/.ivy2/cache/ch.qos.logback/logback-core/jars/logback-core-1.0.13.jar), Attributed(/Users/uqiu/.ivy2/cache/org.slf4j/slf4j-api/jars/slf4j-api-1.7.5.jar))
[success] Total time: 0 s, completed Apr 4, 2016 11:54:57 PM
> show externalDependencyClasspath
[info] List(Attributed(/Users/uqiu/test/lib/guava-18.0.jar), Attributed(/Users/uqiu/.sbt/boot/scala-2.10.6/lib/scala-library.jar), Attributed(/Users/uqiu/.ivy2/cache/ch.qos.logback/logback-classic/jars/logback-classic-1.0.13.jar), Attributed(/Users/uqiu/.ivy2/cache/ch.qos.logback/logback-core/jars/logback-core-1.0.13.jar), Attributed(/Users/uqiu/.ivy2/cache/org.slf4j/slf4j-api/jars/slf4j-api-1.7.5.jar))
[success] Total time: 0 s, completed Apr 4, 2016 11:55:03 PM
> show internalDependencyClasspath
[info] List()
[success] Total time: 0 s, completed Apr 4, 2016 11:55:10 PM

这里我们没有定义子项目, 所以 internalDependencyClasspath 为空, dependencyClasspath 和  externalDependencyClasspath 是一样的.

一般来说我们关心的是 externalDependencyClasspath 的内容, 上面显示的是 List[Attribute[String]] 类型的内容, 为了可读性, 可以用 Shell 脚本或自定义 sbt 任务来格式化显示它.

Shell 脚本显示, 在项目目录下创建 dependencies.sh, 内容如下:

#!/bin/bash

echo "Direct dependencies"
sbt 'show all-dependencies' | gawk 'match($0, /List\((.*)\)/, a) {print a[1]}' | tr -d ' ' | tr ',' '\n' | sort -t ':' | \
  tr ':' '\t' | expand -t 30

echo -e "\nAll dependencies, including transitive dependencies"
sbt 'show managed-classpath' | tr -d ' ' | tr ',' '\n' | gawk 'match($0, /Attributed\((.*)\)/, a) {print a[1]}' | \
  tr -d '()' | sed "s^$HOME/.ivy2/cache/^^g" | sed "s^/jars^^" | \
  gawk -F / '{print $1, $3}' | sort | tr ' ' '\t' | expand -t 30

Mac 下虽安装 gawk, 可用命令  brew install gawk 安装, 并用 chmod +x dependencies.sh 加上可执行属性, 完整命令如下:

brew install gawk
chmod +x dependencies.sh
./dependencies.sh

显示结果大致如下:

Direct dependencies
ch.qos.logback                logback-classic               1.0.13
optional(default)
optional(default)
org.scala-lang                scala-compiler                2.10.6                        scala-tool->default
org.scala-lang                scala-library                 2.10.6
org.scala-lang                scala-library                 2.10.6                        scala-tool->default

All dependencies, including transitive dependencies
                              uqiu
ch.qos.logback                logback-classic-1.0.13.jar
ch.qos.logback                logback-core-1.0.13.jar
org.slf4j                     slf4j-api-1.7.5.jar

自定义 sbt 任务, 在 build.sbt 中加入如下内容:

lazy val versionReport = TaskKey[String]("version-report")

// Add this setting to your project.
versionReport <<= (externalDependencyClasspath in Compile, streams) map {
   (cp: Seq[Attributed[File]], streams) =>
     val report = cp.map {
       attributed =>
         attributed.get(Keys.moduleID.key) match {
           case Some(moduleId) => "%40s %20s %10s %10s".format(
             moduleId.organization,
             moduleId.name,
             moduleId.revision,
             moduleId.configurations.getOrElse("")
           )
           case None           =>
             // unmanaged JAR, just
             attributed.data.getAbsolutePath
         }
     }.mkString("\n")
     streams.log.info(report)
     report
  }

重新运行 sbt 或 reload 之后执行 versionReport 任务, 输出如下:

> versionReport
[info] /Users/uqiu/test/lib/guava-18.0.jar
[info]                           org.scala-lang        scala-library     2.10.6
[info]                           ch.qos.logback      logback-classic     1.0.13
[info]                           ch.qos.logback         logback-core     1.0.13
[info]                                org.slf4j            slf4j-api      1.7.5

这能让我们看到所有的依赖, 但并未显示成树状关系图, 还没有达到本文标题所称的目标, 所以终极办法也是最简单的办法就是不重新发明轮子, 使用现有的插件 https://github.com/jrudolph/sbt-dependency-graph.

sbt 插件显示依赖树

可以在 ~/.sbt/0.13/plugins/plugins.sbt 或项目中新建 project/plugins.sbt 中加入行

addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2")

这时在 sbt 的控制台下就增加了好多任务,

dependencyTree: Shows an ASCII tree representation of the project's dependencies
dependencyBrowseGraph: Opens a browser window with a visualization of the dependency graph (courtesy of graphlib-dot + dagre-d3).
dependencyGraph: Shows an ASCII graph of the project's dependencies on the sbt console
dependencyList: Shows a flat list of all transitive dependencies on the sbt console (sorted by organization and name)
whatDependsOn <organization> <module> <revision>: Find out what depends on an artifact. Shows a reverse dependency tree for the selected module.
dependencyLicenseInfo: show dependencies grouped by declared license
dependencyStats: Shows a table with each module a row with (transitive) Jar sizes and number of dependencies
dependencyGraphMl: Generates a .graphml file with the project's dependencies to target/dependencies-<config>.graphml. Use e.g. yEd to format the graph to your needs.
dependencyDot: Generates a .dot file with the project's dependencies to target/dependencies-<config>.dot. Use graphviz to render it to your preferred graphic format.
ivyReport: let's ivy generate the resolution report for you project. Use show ivyReport for the filename of the generated report

我比较感兴趣的是 dependencyTree(显示像  Maven 的  dependency:tree 那样) 和 dependencyBrowseGraph(打开浏览器显示一个依赖关系图)

> dependencyTree
[info] default:test_2.10:0.1-SNAPSHOT [S]
[info]   +-ch.qos.logback:logback-classic:1.0.13
[info]     +-ch.qos.logback:logback-core:1.0.13
[info]     +-org.slf4j:slf4j-api:1.7.5
[info]
[success] Total time: 0 s, completed Apr 5, 2016 12:29:53 AM

或者执行 dependencyBrowseGraph 后打开一个浏览器

dependencyBroweGraph

还有 dependencyGraph 任务输出为文本图形, dependencyDot 能生成 dot 文件, 等等.

用 sbt-dependency-graph 插件的办法是最强大也是最简单, 而且如果在 ~/.sbt/0.13/plugins/plugins.sbt 加载该插件更可谓是一劳永逸的做法.

参考: 1. https://groups.google.com/forum/#!topic/simple-build-tool/rcPh-lWbDtk
        2. https://github.com/jrudolph/sbt-dependency-graph
        3. http://mikeslinn.blogspot.com/2012/07/bash-script-to-update-all-git.html

 

类别: Scala. 标签: , , . 阅读(144). 订阅评论. TrackBack.

Leave a Reply

Be the First to Comment!

avatar
wpDiscuz