これまではmakeが全てうまくいくという前提でMakefileを書いてきたわけだが、状況によってはそれ以上処理を進めることができないケースというのも出てくる。当然ながらそういった場合にはmakeを終了する必要があるのだが、exitではmakeの実行を終了することができない。今回はmakeを終了するための関数について取り上げる。
レシピにexitを書いてもmakeは終了しない
Linuxを使っているということは当然シェルを使っているわけで、処理を終了させるにはexitと入力すればよいことは誰しもが知っていることだ。しかし、Makefileのレシピ部分にexitと書いたとしても、makeの処理は終了することなく続いていく。
次のMakefileを見てみよう。
test:
@echo exit前
exit
@echo exit後
このMakefileに対して作者が期待するのは、exitの行でmakeの処理が止まることだ。しかし、実行すると実際には次のようになる。
% make
exit前
exit
exit後
%
exitが処理された後もmakeは処理を進めていることがわかる。
これまでの説明でmakeの仕組みを深く理解している場合、この動作は当然だということはわかるだろう。
makeは「@echo exit前」「exit」「@echo exit後」というレシピに対し、それぞれシェルを起動して「echo exit前」「exit」「echo exit後」という文字列を渡す。makeが行うのはここまでで、あとは引き受けたシェルが処理を行う。シェルが「exit」でシェルを終了させても、それは呼び出した側のmakeには影響しないのだ。
makeの動作を変更する関数error
こうした場合に使用する関数としてGNU makeにはerrorという関数が用意されている。この関数は現在主流のプログラミング言語が使用しているerror関数と似ている。エラーメッセージを出力するとともに、makeそのものを終了する。
仲間に分類される関数にwarning関数とinfo関数がある。この3つの関数はまとめて覚えておこう。シンタックスと処理内容をかんたんにまとめると次のようになる。
関数 | シンタックス |
---|---|
error | $(error テキスト) |
warning | $(warning テキスト) |
info | $(info テキスト) |
関数 | 内容 |
---|---|
error | 指定されたテキストの表示や式の評価を行ったのち、makeを終了する。 |
warning | 指定されたテキストの表示や式の評価を行う。 |
info | 指定されたテキストの表示や式の評価を行う。makefile名や行番号は追加されない。 |
makeを止めたいときはerror関数、情報の出力を行いたいだけならwarning関数やinfo関数といった使い分けになる。
error関数、warning関数、info関数を使ってみる
まだ取り上げていない機能なのだが、変数が定義されている場合に処理を行わせたいので「条件付き構文」という機能も使ったMakefileのサンプルを用意した。条件付き構文については近いうちに取り上げるので、今回はそんなものかといった程度に考えておいてもらえれば幸いだ。
MSGERR:= 致命的なエラーです。
MSGWRN:= 注意すべき状態です。
MSGINF:= 通常状態です。
test:
@echo $$\(error\)前
ifneq "$(origin ERROR)" "undefined"
$(error $(MSGERR))
endif
@echo $$\(error\)後
@echo $$\(warning\)前
ifneq "$(origin WARNING)" "undefined"
$(warning $(MSGWRN))
endif
@echo $$\(warning\)後
@echo $$\(info\)前
ifneq "$(origin INFO)" "undefined"
$(info $(MSGINF))
endif
@echo $$\(info\)後
Makefileのうち「ifneq "$(origin ERROR)" "undefined"」の処理を説明しておく。$(origin ERROR)は前回取り上げたようにERROR変数の由来(オリジン)を表示させる処理だ。変数が定義されていなければこの関数の結果はundefinedになる。ifneqはその後の引数が等価でなければ真になるという条件なので、「$(origin ERROR)がundefinedではない場合」ということであり、つまり「ERRORが何らかの形で定義されている場合」という条件ということになる。
ということで、上記Makefileは「ERROR変数が定義されていれば$(error)を実行」「WARNING変数が定義されていれば$(warning)を実行」「INFO変数が定義されていれば$(info)を実行」という内容になっている。
このMakefileを引数でエラーを定義しながら実行すると次のようになる。
% make
$(error)前
$(error)後
$(warning)前
$(warning)後
$(info)前
$(info)後
% make -e ERROR=
Makefile:6: *** 致命的なエラーです。. Stop.
% make -e WARNING=
Makefile:6: 注意すべき状態です。
$(error)前
$(error)後
$(warning)前
$(warning)後
$(info)前
$(info)後
% make -e INFO=
通常状態です。
$(error)前
$(error)後
$(warning)前
$(warning)後
$(info)前
$(info)後
%
説明にある通り、error関数が処理されるとmakeが終了していることがわかる。warning関数やinfo関数の場合にはmakeはそのまま処理を続けていることがわかる。
error関数を使わなくて済むならそれがよいかもしれないが、error関数を使った方がMakefileがシンプルになったり、安全になることもある。こうした機能があることは覚えておこう。
error関数とレシピの実行に注意が必要
実行結果を見てわかるように、error関数を使用する場合にはレシピのコマンドがどこまで処理されるのかに注意が必要だ。上記Makefileの場合、レシピの処理が実際にシェルに渡される前にmakeが終了している。パースして$(error)が実行されるということが明らかになった時点でmakeが終了しているわけで、error関数よりも先に書いてあるレシピは実行されていないのだ。
つまり、error関数よりも前に書いてあるレシピ(コマンド)は必ず実行されるという前提でMakefileを組むと、そうではないので不具合を起こすことになる。Makefileはこのあたりの挙動が人間が追うには少々複雑なので、Makefileを組み上げていく段階で実際に試しながら確認していくことがおすすめだ。これはMakefilenに限った話ではないのだが、ちょっと書いては実行して動作を確認するというのは、着実に作業を進める上で効率の良い方法だ。ぜひ試してもらえればと思う。