GNU make にて、途中でエラーが起きてもそのまま処理を継続させる方法を説明します。


make は途中でエラーが出るとそこで処理を中断します。例えばC言語でプログラムをビルドするとき、途中でコンパイルエラーが出ているのにそのまま続けても意味がないですし、続けてもリンクエラーになりますから。妥当な挙動と言えます。 しかし、makeにて途中でエラーが出ても最後まで続けて欲しい場合もあります。例えば、プロジェクトの立ち上げ時でまだビルドが通らない状況だけどひとまず現時点でコンパイルエラーが何個起きるか数えたいときや、静的解析ツールを実行していて途中で指摘があっても全ファイルチェックしたいとき、などです。 その場合の対処法を2パタン説明します。

make のオプション -k (--keep-going)

基本的にはこれを使えばOKです。オプション名通り、エラーが起きても処理を続けてくれます。具体的には、ターゲットの生成に失敗しても次のターゲットを生成しに行く、という挙動になります。 例えばC言語でプログラムをビルドするときは下記のようなパタンルールが記述されていると思います。

1
2
%.o : %.c
    $(CC) $(CFLAGS) -c $< -o $*.o

これは、拡張子 .o で終わるターゲットを作成するために $(CC) 使って .cファイルをコンパイルせよ、という意味です。 hoge.c fuga.c piyo.cという3つのファイルがあったとすると、ターゲットはhoge.o fuga.o piyo.oとなります。

これを順番に生成していくわけですが、もしfuga.oのコンパイルに失敗した場合、デフォルトではそこで make は中断されます。 ですが -k(--keep-going) をつけておくと、fuga.cのコンパイルに失敗してもhoge.cpiyo.cもコンパイルが実行されていきます。

終了ステータスを「:」で0(正常終了)にする

たいていは上記の --keep-going で事足りるのですが、それではダメなケースもあります。 例えばコンパイルと同時に静的解析ツールを実行する場合を考えます。パタンルールを下記のように書いたとします。$(LINT)で静的解析を実行するという記述ですね。

1
2
3
%.o : %.c
    $(LINT) $(LINTFLAGS) $<
    $(CC)   $(CFLAGS) -c $< -o $*.o

もしここで、静的解析ツールが問題のある記述を発見しエラーを返してきたとします。 --keep-going をつけていれば問題なくコンパイルも実行されそうですが、実はそうではありません。 --keep-going はターゲットの生成に失敗しても次のターゲットを生成しにいく、というオプションです。 ターゲットの生成に必要な処理を全部実行するというわけではありません

この場合ですと、最初の$(LINT)の静的解析ツールでエラーだった場合、次の行の$(CC)のコンパイルは実行されずにこのターゲットの生成は終了してしまいます。 わかりづらいと思うので例を挙げます。hoge.c fuga.c piyo.cという3つのファイルがあり、fuga.cに静的解析ツールがエラーを出すコードが含まれていた場合を考えます。

hoge.cは静的解析もコンパイルも通ります。そして問題のfuga.cに処理が移ったとき、静的解析ツール$(LINT)にてエラーが出ます。 ここでfuga.cの処理は諦めてその次のpiyo.cへ処理が移ります。つまりfuga.cのコンパイルは実行されません

このような問題に対処するにはどうすればいいか。静的解析ツールの終了ステータスを必ず正常終了になるように工夫してあげればOKです。 もう少し正確にいうと、静的解析ツールが失敗したときには、必ず成功するコマンドを実行するようにします。具体的には下記のようにします。 $(LINT)の行の最後の|| :が付いていますね。

1
2
3
%.o : %.c
    $(LINT) $(LINTFLAGS) $< || :
    $(CC)   $(CFLAGS) -c $< -o $*.o

||は論理和ですので、左の処理が失敗したときだけ右の処理を実行します。:は「何もしない」という処理ですので、必ず成功します。 ですので、結果的に静的解析が必ず正常終了したように見えます。こうすることで、仮に静的解析ツールがエラーを返しても、期待通りコンパイルまで実行されるようになります。

Makefile の例

この方法を使っている makefile の例を下記に挙げます。 https://github.com/nagayasu-shinya/gnu-make-framework-zen 該当箇所だけ抜粋してみます。こんな感じです。ご参考まで。

1
2
3
4
5
IGNORE_EXIT_STATUS = || :

%.o : %.c
    $(SPLINT) $(SPLINT_FLAGS) $(filter -I%,$(CFLAGS)) $< $(IGNORE_EXIT_STATUS)
    $(CCACHE) $(CC) $(CFLAGS) -c $< -o $*.o

参考書籍はこれです。

まとめ

今回は、makeで途中でエラーが発生してもすべての処理を実行する方法を述べました。基本的には makeのオプション -k (--keep-going)でOKです。 ただしターゲットを生成するのに複数のコマンドを実行する必要がああるときは、:コマンドをうまく使ってエラーが発生しないようにしなければいけません。意外と見落としがちなことですので注意してください!!