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 的输出
1 2 3 4 5 6 7 8 9 |
> show dependencyClasspath [info] List(Attributed(/Users/yanbin/test/lib/guava-18.0.jar), Attributed(/Users/yanbin/.sbt/boot/scala-2.10.6/lib/scala-library.jar), Attributed(/Users/yanbin/.ivy2/cache/ch.qos.logback/logback-classic/jars/logback-classic-1.0.13.jar), Attributed(/Users/yanbin/.ivy2/cache/ch.qos.logback/logback-core/jars/logback-core-1.0.13.jar), Attributed(/Users/yanbin/.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/yanbin/test/lib/guava-18.0.jar), Attributed(/Users/yanbin/.sbt/boot/scala-2.10.6/lib/scala-library.jar), Attributed(/Users/yanbin/.ivy2/cache/ch.qos.logback/logback-classic/jars/logback-classic-1.0.13.jar), Attributed(/Users/yanbin/.ivy2/cache/ch.qos.logback/logback-core/jars/logback-core-1.0.13.jar), Attributed(/Users/yanbin/.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, 内容如下:
1 2 3 4 5 6 7 8 9 10 |
#!/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 加上可执行属性, 完整命令如下:
1 2 3 |
brew install gawk chmod +x dependencies.sh ./dependencies.sh |
显示结果大致如下:
1 2 3 4 5 6 7 8 9 10 11 12 13 |
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 yanbin 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 中加入如下内容:
1 2 3 4 5 6 7 8 9 10 11 12 13 14 15 16 17 18 19 20 21 22 |
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 任务, 输出如下:
1 2 3 4 5 6 |
> versionReport [info] /Users/yanbin/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 中加入行
1 |
addSbtPlugin("net.virtual-void" % "sbt-dependency-graph" % "0.8.2") |
这时在 sbt 的控制台下就增加了好多任务,
1 2 3 4 5 6 7 8 9 10 |
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(打开浏览器显示一个依赖关系图)
1 2 3 4 5 6 7 |
> 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 后打开一个浏览器
还有 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
本文链接 https://yanbin.blog/show-sbt-dependency-tree/, 来自 隔叶黄莺 Yanbin Blog
[版权声明] 本文采用 署名-非商业性使用-相同方式共享 4.0 国际 (CC BY-NC-SA 4.0) 进行许可。