Go パッケージ/モジュールの依存関係可視化ツールを作ってみた

no extension

ついカッとなってやった。 反省はしない。

この手のツールって誰もが一度は書いてみたくなると思うけど,以前自作したツールがイマイチで,他の方が作ったツールも微妙だったので,最初から作り直してみた。

前のツール では go mod graph コマンドの出力をパイプで繋いで処理してたけど,今回はツール内で go list コマンドを呼び出している。 したがって Go コンパイラがインストール済みであることが前提条件となる。

-h オプションで簡単なヘルプを表示する。

$ depm -h
Visualize depndency packages and modules.

Usage:
  depm [flags]
  depm [command]

Available Commands:
  help        Help about any command
  module      analyze depndency modules
  package     analyze depndency packages
  version     print the version number

Flags:
      --cgo-enabled string   set CGO_ENABLED environment variable
      --debug                for debug
      --dot                  output by DOT language
      --dot-config string    config file for DOT language
      --goarch string        set GOARCH environment variable
      --goos string          set GOOS environment variable
  -h, --help                 help for depm

Use "depm [command] --help" for more information about a command.

depm module コマンドならこんな感じに使える。

$ depm module github.com/spiegel-im-spiegel/depm | jq .
[
  {
    "Module": {
      "Path": "github.com/spf13/cobra@v1.1.1",
      "Packages": [
        "github.com/spf13/cobra"
      ]
    },
    "Deps": [
      {
        "Path": "github.com/spf13/pflag@v1.0.5",
        "Packages": [
          "github.com/spf13/pflag"
        ]
      }
    ]
  },
  {
    "Module": {
      "Path": "github.com/spiegel-im-spiegel/depm@v0.1.0",
      "Packages": [
        "github.com/spiegel-im-spiegel/depm",
        "github.com/spiegel-im-spiegel/depm/dependency",
        "github.com/spiegel-im-spiegel/depm/dependency/modjson",
        "github.com/spiegel-im-spiegel/depm/dependency/pkgjson",
        "github.com/spiegel-im-spiegel/depm/dotenc",
        "github.com/spiegel-im-spiegel/depm/ecode",
        "github.com/spiegel-im-spiegel/depm/facade",
        "github.com/spiegel-im-spiegel/depm/golist",
        "github.com/spiegel-im-spiegel/depm/modules",
        "github.com/spiegel-im-spiegel/depm/packages"
      ]
    },
    "Deps": [
      {
        "Path": "github.com/spiegel-im-spiegel/gocli@v0.10.3",
        "Packages": [
          "github.com/spiegel-im-spiegel/gocli/exitcode",
          "github.com/spiegel-im-spiegel/gocli/rwi"
        ]
      },
      {
        "Path": "github.com/spf13/cobra@v1.1.1",
        "Packages": [
          "github.com/spf13/cobra"
        ]
      },
      {
        "Path": "github.com/spiegel-im-spiegel/errs@v1.0.2",
        "Packages": [
          "github.com/spiegel-im-spiegel/errs"
        ]
      },
      {
        "Path": "golang.org/x/tools@v0.0.0-20201031021630-582c62ec74d0",
        "Packages": [
          "golang.org/x/tools/go/ast/astutil",
          "golang.org/x/tools/imports",
          "golang.org/x/tools/internal/event",
          "golang.org/x/tools/internal/event/core",
          "golang.org/x/tools/internal/event/keys",
          "golang.org/x/tools/internal/event/label",
          "golang.org/x/tools/internal/fastwalk",
          "golang.org/x/tools/internal/gocommand",
          "golang.org/x/tools/internal/gopathwalk",
          "golang.org/x/tools/internal/imports"
        ]
      },
      {
        "Path": "github.com/BurntSushi/toml@v0.3.1",
        "Packages": [
          "github.com/BurntSushi/toml"
        ]
      },
      {
        "Path": "github.com/emicklei/dot@v0.15.0",
        "Packages": [
          "github.com/emicklei/dot"
        ]
      }
    ]
  },
  {
    "Module": {
      "Path": "golang.org/x/mod@v0.3.0",
      "Packages": [
        "golang.org/x/mod/module",
        "golang.org/x/mod/semver"
      ]
    },
    "Deps": [
      {
        "Path": "golang.org/x/xerrors@v0.0.0-20200804184101-5ec99f83aff1",
        "Packages": [
          "golang.org/x/xerrors",
          "golang.org/x/xerrors/internal"
        ]
      }
    ]
  },
  {
    "Module": {
      "Path": "golang.org/x/tools@v0.0.0-20201031021630-582c62ec74d0",
      "Packages": [
        "golang.org/x/tools/go/ast/astutil",
        "golang.org/x/tools/imports",
        "golang.org/x/tools/internal/event",
        "golang.org/x/tools/internal/event/core",
        "golang.org/x/tools/internal/event/keys",
        "golang.org/x/tools/internal/event/label",
        "golang.org/x/tools/internal/fastwalk",
        "golang.org/x/tools/internal/gocommand",
        "golang.org/x/tools/internal/gopathwalk",
        "golang.org/x/tools/internal/imports"
      ]
    },
    "Deps": [
      {
        "Path": "golang.org/x/mod@v0.3.0",
        "Packages": [
          "golang.org/x/mod/module",
          "golang.org/x/mod/semver"
        ]
      }
    ]
  }
]

出力は JSON 形式で。 パッケージ(モジュール)パスを省略するとカレント・ディレクトリを調べる1

--dot オプションを付けると DOT 言語形式で出力するので,そのまま dot コマンドに渡して関連図を作成できる。

$ depm module --dot github.com/spiegel-im-spiegel/depm | dot -Tpng -o output.png

出力はこんな感じ。

output.png
output.png

TOML 形式で以下のような設定ファイルを作れば(DOT 言語の仕様にしたがって)見た目を多少変えることができる。

[node]
  fontname = "Inconsolata"
[edge]
  color = "red"

これで

$ depm module --dot --dot-config dot-config.toml github.com/spiegel-im-spiegel/depm | dot -Tpng -o output2.png

とかすれば

output2.png
output2.png

のような見た目にできる。 あと -c オプションでモジュールの最新バージョンの取得もできたりする。

depm package コマンドにするとモジュール単位ではなくパッケージ単位で依存関係を整理する。

$ depm package github.com/spiegel-im-spiegel/depm

結構スゴい出力になるので,結果は割愛する(笑) depm package コマンドに -s-i オプションを付けると Go の標準ライブラリや internal パッケージも対象になるので,本当にワケワカラン出力になる。

output3.png
output3.png

勢いで書いてろくにテストもしないでリリースしたが,あとはノンビリ手を入れていこう。

参考図書

photo
プログラミング言語Go (ADDISON-WESLEY PROFESSIONAL COMPUTING SERIES)
Alan A.A. Donovan (著), Brian W. Kernighan (著), 柴田 芳樹 (翻訳)
丸善出版 2016-06-20
単行本(ソフトカバー)
4621300253 (ASIN), 9784621300251 (EAN), 4621300253 (ISBN), 9784621300251 (ISBN)
評価     

著者のひとりは(あの「バイブル」とも呼ばれる)通称 “K&R” の K のほうである。この本は Go 言語の教科書と言ってもいいだろう。

reviewed by Spiegel on 2016-07-13 (powered by PA-APIv5)


  1. パッケージ(モジュール)パスを省略した場合は,ツール内部で go list コマンドに all を渡している。 ↩︎