本文
git リポジトリの Makefile をグラフ化するこんな画像になります。
(非常に横に長い画像です)
以下の Makefile を任意のプロジェクトに置き、後は Makefile のフォーマットに従ってファイル同士の依存関係を記述するだけです。
.PHONY: help
help: # シャープコメントが付いている Make タスク一覧を出力する
@cat $(MAKEFILE_LIST) \
| grep -E '^[^:# ]+:.*?# .*$$' \
| sort \
| awk 'BEGIN {FS = ":.*?# "}; {printf "\033[36m%-30s\033[0m %s\n", $$1, $$2}'
.PHONY: clean
clean: # 生成ファイルの削除
@rm -f deps.dot deps.png deps-serialized.txt
deps.dot: Makefile # 依存グラフの dot ファイル
@echo 'digraph deps {' > deps.dot
@cat $(MAKEFILE_LIST) \
| grep -E '^[^:# ]+:' \
| grep -vE '^(\.PHONY|help|deps\.(dot|png)|deps-serialized\.txt):' \
| sed -E 's/#.*$$//' \
| sed -E 's/\\/\\\\/g' \
| sed -E 's/"//g' \
| awk 'BEGIN{FS="[ \t]*:?[ \t]*"} { for(i=2;i<=NF;i++){ if ($$i!="") print "\"" $$1 "\" -> \"" $$i "\"" } }' \
>> deps.dot
@echo '}' >> deps.dot
deps.png: deps.dot # 依存グラフを可視化した画像
@dot -Tpng deps.dot -o deps.png
deps-serialized.txt: deps.dot # 依存グラフを直列化したリスト
@cat deps.dot \
| grep -- '->' \
| awk -F'->' '{print $$2,$$1}' \
| tsort \
| tr -d '"' \
> deps-serialized.txt
Make を使わないプロジェクトでも、ファイル同士の依存関係を管理するためだけに Makefile を使ってもいいかもしれません。
make, Graphviz の dot, tsort コマンドなどが必要です。
後はどうでもいい話
今日の仕事でファイル名の連番プレフィクスで順序を管理されているファイルの順序を入れ替える作業があった。
20_xxx というファイルの後だが、21_yyy の前というファイルを作らねばならなかった。まさか 20.5_zzz というファイルを作るわけにもいかないので、既存ファイルをリネームしたが、これがファイルのリネームにセンシティブなシステム/プログラムだと頭を抱えることになっただろう。イケてない。
振り返ると日頃から順序、少し拡張して依存関係 (非閉路有向グラフ) の扱いが雑だ。しっかり考えれば難しい問題ではないのに、手間を惜しんで行き当たりばったりの対応をしている。ベストプラクティスを探そうと思って書いたのが今晩。
ちなみに、順序関係を番号で管理するイケてないやり方に対する揶揄で「BASIC の行番号かよ」というフレーズをどこかで聞いたが、とても気に入ってる。
ファイルの順序/依存関係を管理する
ファイル名で順序を管理しようとすると番号が足りなくなる。
リネームすると困る場合だと最悪死ぬ。
(脇にそれますが、みなさん RDB で順序を管理しようとする時、どうやってますか? 番号振り直ししか思いつかなくて困ってます……)
順序/依存関係の変更で既存ファイルに影響を与えたくないので、順序/依存関係を扱うファイルを用意しようと考えた。(行毎にファイル名を書いた file_list.txt みたいなファイル)
単純な順序ならこれでいいかもしれないが、大抵はグラフで表現したくなる(気がする)。依存関係といえば Makefile だという発想が自分の中のイメージだったので、なんとかして Makefile を利用しようということになった。
それに Makefile なら Make 使わなさそうなプロジェクトにポンと置いてもとりあえず make 叩いてくれそうな気がする。
グラフ化する
Graphviz で。
直列化する
トポロジカルソート実装しなくても tsort というまんまのコマンドがある。普段中々使わないから忘れられがち。
まとめ
というわけで Makefile にシェルスクリプト書いて、変換を書ければ出来上がり。
a: b c
b: d
c: d
みたいに、Make ターゲットの実行部分を書かず、依存関係だけを Makefile で管理することもできるので気に入っている。
可視化とメンテしやすさが同時に手に入ってウマウマ。