Graphvizを使えば ○→○ な関係の可視化を簡単に自動化できる、例えば自作RPMの依存関係とか

# Graphviz
Graphviz と言えば、図を生成するために [PlantUML](http://plantuml.com/ja/) や [Doxygen](http://www.doxygen.jp/) の内部で使用されていることで有名なので、ユーザーが直接使うソフトウェアというイメージは薄いかもしれません。

しかし [DOT言語](https://ja.wikipedia.org/wiki/DOT%E8%A8%80%E8%AA%9E)という表記法で簡単なテキストファイルを作ってコマンドを実行するだけで図を生成してくれる、何かと便利なツールだったりします。

![graphviz_sample.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/549a4813-2d9f-f612-08eb-5feaee4e488f.png)

上記は PlantUML と Doxygen で生成された図の公式サンプルです。こういう複雑で綺麗な図を作るのは大変ですが、単純な ○→○ だけで表現できる図なら簡単に作ることができます。


# ハローワールド的な例

例えば下図の左のように「1」と「2」の関係を記しただけの簡単なテキストが、自動的に右の図に変換されます。

![graphviz_test.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/ef75a473-a7a2-7cea-3692-644acfb7d004.png)

2本の線が重ならないように自動的に避けて配置されていますが、このように配置や大きさもお任せできるので、手作業を排除した完全自動化を実現することができます。

公式ドキュメント
https://www.graphviz.org/documentation/

# 使用例: 自作RPMの依存関係を可視化する

RPM パッケージには「どのパッケージに依存するか」がメタ情報として定義されています。yum などのパッケージマネージャはこれを参照して、依存パッケージを自動的にインストールしてくれます。

しかし自作パッケージが増えてくると、どれがどれに依存しているかを忘れてしまいがちです。もちろん個別にコマンドで確認することはできるのですが、全パッケージの依存関係図を自動生成できればと思って試してみました。

単純な **○→○** だけの図でも、依存関係を可視化するという目的には十分です。

## RPMパッケージのSPECファイル

RPM パッケージを自作する場合は SPEC ファイルというテキストファイルにメタ情報を記述して、プログラムやファイルと一緒にビルドして配布します。
依存パッケージの情報は、以下のように`Requires`で始まる行で SPEC ファイルに定義します。

```Spec:myapp.spec
Requires: nginx >= 1.14
Requires: myapp-lib >= 0.1.0
Requires(post): jq >= 1.5
```

つまり SPEC ファイルから`Requires`で始まる行のパッケージ名を抽出するスクリプトを作るだけで、依存関係を表記した DOT ファイルを作ることができます。

## DOTファイル生成スクリプト

言語は何でもいいのですが、bash スクリプトならこんなに簡単になります。

```Bash:
#!/bin/bash

# *****************************************************************************
# 指定ディレクトリ内の全SPECファイルから「Requires」で始まる行に定義された依存
# パッケージ名を抽出し、依存関係をDOT言語の表記で出力する。
#
# $1  SPECファイルがあるディレクトリ
function print_digraph() {
  local spec_file=
  local required_package=

  echo "digraph {"
  cd "${1}"
  for spec_file in $(ls -1 *.spec)
  do
    grep "^\s*Requires.*:" "${spec_file}" | awk '{print $2}' |
        while IFS= read required_package
        do
          # 依存元パッケージ名はSPECファイルから拡張子を除去した名前で、
          # '  "依存元パッケージ名" -> "依存先パッケージ名"' を出力する。
          echo "  \"${spec_file%.*}\" -> \"${required_package}\""
        done
  done
  echo "}"
}

###############################################################################
# $1  SPECファイルがあるディレクトリ。
# $2  出力する画像ファイル。

# 全SPECファイルからDOTファイルを生成して、DOTファイルから図を生成する。
_dot=$(print_digraph "${1}")
dot -T png -o "${2}" <<<"${_dot}"

# 生成された画像ファイルを表示して終了する。
eog "${2}" &
exit 0
```

## 実行結果
SPEC ファイルを 10個作成してスクリプトを実行したら、下図の画像が一瞬で生成されました。

![temp.png](https://qiita-image-store.s3.ap-northeast-1.amazonaws.com/0/169872/247026f5-cfe1-ffdb-a916-250cde14017c.png)


関数内で生成した DOT 言語のテキストは以下のとおりです。

```
digraph {
  "myanim-lib" -> "mybase-lib"
  "myapp-lib" -> "myftp-lib"
  "myapp-lib" -> "myhttp-lib"
  "myapp-lib" -> "myfile-lib"
  "myapp-lib" -> "myui-lib"
  "myapp" -> "nginx"
  "myapp" -> "myapp-lib"
  "myapp" -> "jq"
  "myfile-lib" -> "mybase-lib"
  "myftp-lib" -> "mynetwork-lib"
  "myhttp-lib" -> "mynetwork-lib"
  "mynetwork-lib" -> "mybase-lib"
  "mynetwork-lib" -> "NetworkManager"
  "myui-lib" -> "mybase-lib"
  "myui-lib" -> "myvideo-lib"
  "myui-lib" -> "myanim-lib"
  "myvideo-lib" -> "mybase-lib"
  "myvideo-lib" -> "ffmpeg"
}
```

# あとがき

Graphviz が超優秀なソフトウェアなので、ご覧のとおりの簡単なスクリプトを作っただけで可視化の自動化ができちゃいました。工数対効果が大変高くてお得です。