Makefile:依存関係(存在しないファイル)
Makefileの依存関係と同じ書き方をするが、:の左側が実際のファイルではなくただの文字列であることもある。Makefileで次のように書かれているところを見てみよう。
存在しないファイルが指定されているサンプル
clean:
rm -f "$(CMD)"
rm -f *.o
rm -f *.ilk
rm -f *.pdb
Makefileがあるディレクトリにはcleanという名前のファイルはない。しかも、これが依存するファイルも指定されていない。Makefileでは、このように存在しないファイルを指定するような書き方もできる。このような書き方をする時は、makeコマンドに直接この名前を指定して実行する目的で使われたりする(もちろんそれ以外の使い方をされることもある)。
次の実行例を見てみよう。
make cleanの実行サンプル
PS C:\Users\daichi\Documents\vscode-csv2tsv> make clean
rm -f "csv2tsv.exe"
rm -f *.o
rm -f *.ilk
rm -f *.pdb
PS C:\Users\daichi\Documents\vscode-csv2tsv>
make cleanのように、makeの引数に依存関係の左側の名前を指定すると、その依存関係から処理が行われる。依存するファイルが指定されていればタイプスタンプを比較して、実行する必要があれば実行し、必要がなければ実行しない。
Makefile:依存関係(複数ファイル)
今度は、Makefileの次の記述を見てみよう。
複数のファイルに依存しているサンプル
$(CMD): $(OBJS)
$(CC) $(CFLAGS) -o $(CMD) $(OBJS)
依存関係は1対1である必要はない。1つのファイルが2つのファイルから生成されていれば、依存先は2つのファイルということになる。先ほどの記述を、変数を展開すれば次のようになる。
複数のファイルに依存しているサンプル
csv2tsv.exe: csv2tsv.o util.o
clang -g -o csv2tsv.exe csv2tsv.o util.o
csv2tsv.exeという実行ファイルはcsv2tsv.oとutil.oから生成されるので、このビルドは妥当というか、よくある書き方だ。
Makefile:依存関係(ツリー構造)
これまでの依存関係の書き方は、1つのMakefileの中に混在することができる。今回のケースを見てみよう。次の依存関係だ(タブからはじまるコマンド行は削除してある)。
依存関係
build: $(CMD)
$(CMD): $(OBJS)
csv2tsv.o: csv2tsv.c
util.o: util.c
変数を展開すると次のようになる。
依存関係
build: csv2tsv.exe
csv2tsv.exe: csv2tsv.o util.o
csv2tsv.o: csv2tsv.c
util.o: util.c
この依存関係をツリー構造に整理すると次のようになる。
依存関係(ツリー構造)
[build]
└── csv2tsv.exe
├── csv2tsv.o
│ └── csv2tsv.c
└── util.o
└── util.c
こんな感じで、csv2tsv.exeを生成するためにファイルの依存関係が存在しており、それを整理してmakeコマンドが処理を行ってくれる。
Makefileに記述する依存関係は、基本的に上記で説明したようなルールがベースになっている。これだけのルールだが、これだけでかなりたくさんの依存関係を整理し、そしてmakeコマンドから操作できるのだ。makeとMakefileはよくできているのである。
makeは開発以外にも使えるツール
ここで説明したMakefileの規則はとても基本的なものだが、これがMakefileのルールの本質だ。これを理解しておくと、Makefileを書けるようになる。ファイルの依存関係を整理するのは結局、開発者自身だ。Makefileの書き方をちゃんと理解し、逆にMakefileに記述することで依存関係を整理するという方法も有用だ。
本連載ではCのソースコードをビルドするという目的でmakeとMakefileを使うが、このツールはC言語の開発に限らず、さまざまな開発や日常のツールとして使えることは強調しておきたい。このツールを使えると、いろんな操作をmakeコマンドで行うようにもなるくらいに便利なのだ。使ったことがなければ、これを機に取り組んでもらえればと思う。