sbt-dependency-graphの高度なカスタマイズ:出力形式とフィルタリング戦略

はじめに

sbt-dependency-graphは、Scalaプロジェクトにおける依存関係を可視化・分析するための強力なsbtプラグインです。本稿では、標準機能を超えたカスタム出力の生成方法や、複雑な依存ツリーを効率的に管理するための高度なフィルタリング手法について解説します。

プラグインの導入と基本設定

使用を開始するには、まずproject/plugins.sbtに以下の行を追加します。

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

適用後、次の主要タスクが利用可能になります:

  • dependencyTree:階層的な依存ツリーを表示
  • dependencyList:平文化された依存リスト
  • dependencyStats:モジュール数や重複バージョンの統計情報
  • licenseInfo:ライセンス一覧

出力形式の拡張とカスタマイズ

デフォルトのテキスト出力に加え、外部ツール連携可能な形式への対応が可能です。

DOT形式によるグラフ生成

Graphvizとの連携により、視覚的に理解しやすい依存グラフを作成できます。次のようにヘッダーとノードラベルをカスタマイズ可能です。

dependencyDotHeader := 
  """
  |digraph "project-deps" {
  |  graph [rankdir="LR", fontsize=12];
  |  node [shape=ellipse, fontname="Meiryo", margin="0.2"];
  |  edge [arrowhead=vee, color="#555"];
  """.stripMargin

dependencyDotNodeLabel := { (org, name, ver) =>
  s"""<table border="0" cellborder="1" cellspacing="0">
      <tr><td colspan="2" bgcolor="#f0f8ff"><b>$name</b></td></tr>
      <tr><td>org</td><td>$org</td></tr>
      <tr><td>ver</td><td>$ver</td></tr>
      </table>"""
}

実行コマンド:
sbt dependencyDottarget/scala-*/dependency-graph.dot を生成

HTMLベースのインタラクティブビュー

ブラウザ上で探索可能な依存関係図を生成するタスクも提供されています。

dependencyBrowseGraph := Def.task {
  val htmlPath = (Compile / target).value / "dependency-graph.html"
  val dotContent = rendering.DOT.dotGraph(
    (moduleGraph in Compile).value,
    dependencyDotHeader.value,
    dependencyDotNodeLabel.value,
    rendering.DOT.LabelTypeHtml
  )
  IO.write(htmlPath, DagreHTML.template(dotContent))
  sys.process.Process("open", Seq(htmlPath.toURI.toString)).!
  htmlPath
}.value

高度なフィルタリング戦略

大規模プロジェクトでは不要な依存を除外することで、分析の焦点を絞ることが重要です。

特定組織の除外

たとえば社内ライブラリ(例: com.internal.*)を除外する場合:

dependencyTree := {
  val rawGraph = (moduleGraph in Compile).value
  val filtered = GraphTransformations.filter(rawGraph) { module =>
    !module.id.organisation.startsWith("com.internal")
  }
  rendering.AsciiTree.asciiTree(filtered)
}

Scala標準ライブラリの制御

Scalaのコアライブラリを表示対象から除外する設定:

filterScalaLibrary in Global := true

これをfalseにすると、暗黙的に含まれるscala-libraryも明示的に表示されます。

逆依存検索(whatDependsOn)

あるモジュールがどのプロジェクトから参照されているかを調査できます。

// sbtシェル内での使用例
sbt> whatDependsOn org.typelevel cats-core _ 

上記コマンドは、cats-coreを利用しているすべてのモジュールを検出します。

ファイル出力と共有

コンソール出力をファイルに保存する機能により、文書化やチーム内共有が容易になります。

// build.sbt内に定義
lazy val exportDeps = taskKey[Unit]("Export dependency tree to file")

exportDeps := {
  val log = streams.value.log
  val tree = (dependencyTree in Compile).value
  val file = (Compile / target).value / "dependencies.tree"
  IO.write(file, tree)
  log.info(s"Dependency tree saved to ${file.getAbsolutePath}")
}

実践的な設定例

以下は、カスタムフィルタと出力形式を組み合わせた総合的な設定例です。

import net.virtualvoid.sbt.graph._

// 全ての依存タスクに共通設定を適用
inConfig(Compile)(Seq(
  dependencyDotHeader := """|digraph G {
                             |  rankdir=TB;
                             |  node[shape=box, style=rounded, fontname="Helvetica"]
                             |""".stripMargin,

  dependencyTree := {
    val graph = moduleGraph.value
    val pruned = GraphTransformations.pruneTransitiveDependencies(graph)
    val noTest = GraphTransformations.excludeConfigurations(pruned, Set("test"))
    rendering.AsciiTree.asciiTree(noTest)
  }
))

トラブルシューティング

出力が肥大化する場合

  • filterScalaLibrary := trueで言語ランタイムを除外
  • excludeConfigurationstestprovidedスコープを除外
  • カスタムGraphTransformationで不要な組織名をフィルタ

HTML生成が失敗する場合

主な原因は出力ディレクトリの権限不足またはパスの不正です。targetディレクトリの存在と書き込み権限を確認してください。

タグ: sbt Scala dependency-management Graphviz DOT

6月18日 23:00 投稿