前回は、Makefileにおける変数の基本を取り上げた。変数が再起的に評価されるというMakefile特有の動きを理解してもらえたのではないかと思う。それを踏まえ、今回は環境変数とMakefileの変数について取り上げる。これらは特にワンライナーでコマンドを実行する際に大切な部分なので、きちんと理解しておいていただきたい。
環境変数
LinuxのようなUNIX系のOSでは、環境変数がとても重要だ。環境変数はプロセス間を受け継がれていくもので、いくつかの挙動の判断基準になっている。例えば、環境変数LANGの値を変更すると、コマンドが利用するシステム標準関数が返す結果が変わってくる。英語系の設定にすれば英語系の結果が返ってくるし、日本語系の設定にすれば日本語系の結果が返ってくるのだ。環境変数の影響範囲は広く、基本的にユーザーが起動するコマンドやアプリケーションに伝播していく。
次のサンプルは、シェルで環境変数のいくつかを表示したものだ。
% echo $HOME
/Users/daichi
% echo $LANG
ja_JP.UTF-8
% echo $PWD
/Users/daichi/Documents/lwt/20220327/stuffs
% echo $SHELL
/bin/zsh
% echo $TERM
xterm-256color
% echo $USER
daichi
%
Makefileにおける環境変数はどうかるかと言うと、環境変数はそのままMakefileの変数になる。そして、その変数はMakefileに書かれるワンライナーコマンドに対して引き継がれていく。
まず、次のMakefileを見てみよう。
test:
echo $(HOME)
echo $(LANG)
echo $(PWD)
echo $(SHELL)
echo $(TERM)
echo $(USER)
実行すると次のようになる。
% make
echo /Users/daichi
/Users/daichi
echo ja_JP.UTF-8
ja_JP.UTF-8
echo /Users/daichi/Documents/lwt/20220327/stuffs
/Users/daichi/Documents/lwt/20220327/stuffs
echo /bin/sh
/bin/sh
echo xterm-256color
xterm-256color
echo daichi
daichi
%
少なくとも、環境変数がMakefileにおける変数として使えていることを確認できる。
では、この変数がMakefileから(正確にはmakeから)起動されることになるコマンド(正確にはシェル)に引き継がれるかどうかを確認してみよう。
次のようなMakefileを用意する。
test:
echo $${HOME}
echo $${LANG}
echo $${PWD}
echo $${SHELL}
echo $${TERM}
echo $${USER}
上記のような書き方をした場合、Makefileにおいて変数参照は行われず、シェルに行ってからそこで変数として評価されることになる。
実行すると次のようになる。
% make
echo ${HOME}
/Users/daichi
echo ${LANG}
ja_JP.UTF-8
echo ${PWD}
/Users/daichi/Documents/lwt/20220327/stuffs
echo ${SHELL}
/bin/zsh
echo ${TERM}
xterm-256color
echo ${USER}
daichi
%
makeから起動されるコマンド(正確にはシェルと、さらにシェルから起動されるコマンド)にも同じ環境変数が引き継がれていることがわかる。この挙動は、UNIX系OSとしては基本的なものだ。
Makefileにおいて環境変数を変更したり、設定したりすると、それはMakefileに記載されているコマンドに影響を与える。明示的に狙っている場合にはそれでよいのだが、よくわからずに設定しているのであれば、いったん考えた方がよい。環境変数はMakefileから起動させるコマンドに影響を与えてしまうからだ。
変数の追加
前回、変数を設定する基本的な方法として「=」「:=」「?=」を取り上げた。今回はこれらに加えて「+=」という設定方法を紹介しよう。
まずは、動作を見てもらうのがわかりやすいだろう。「+=」は変数に値を追加するための指定だ。次のようなMakefileを用意する。
val= a
val+= b
val+= c
test:
echo $(val)
実行すると次のようになる。
% make
echo a b c
a b c
%
「+=」で値を追加していることがわかる。このMakefileは、次のような記述と同じことだ。
val= a
val:= $(val) b
val:= $(val) c
test:
echo $(val)
変数に値を追加するために、わざわざ「+=」という専用の書き方を用意しているのには、とても意味がある。今後、本連載で紹介する予定の機能を使う段階になってくると、「+=」がいかに便利な記述であるかがわかるはずだ。とりあえず、今は「こういった書き方があり、Makefile的には必要なものなんだな」くらいに思っておいてもらえればよい。
なお、「+=」を使った先ほどのMakefileは、次のように書き換えることはできない。
val= a
val= $(val) b
val= $(val) c
test:
echo $(val)
これは、Makefileの変数を理解しているかどうかを試す良い判断ポイントにもなるので、ちょっと考えてみよう。上記のMakefileは決して動くことはない。
実行しようとすると次のようになる。
% make
Makefile:4: *** Recursive variable `val' references itself (eventually). Stop.
%
「=」による変数代入は参照時に評価される。つまり、上記の書き方では$(val)への参照がループしてしまうのだ。「:=」であれば定義時に値が確定されるので、こうしたループが発生しない。これが「=」と「:=」の違いだ。この違いを自然と使い分けられるようになっているなら、すでに相当にMakefileを理解していることになる。
環境変数の追加:よくある使い方
環境変数と、変数の追加の2つは、ある用途においてよく使われるパターンがある。プログラミングツールとしてMakefileを使う、という本来の使い方をするときに利用されることが多い。どういった使われ方をするのか、少しだけ見ておこう。
まず、次のMakefileをご覧いただきたい。
test:
echo "$(CFLAGS)"
「CFLAGS」というのは、C言語で開発されたソースコードをビルドする場合に使われることが多い環境変数だ。
このMakefileを実行すると、次のようになる。
% export CFLAGS="-I/usr/include"
% make
echo "-I/usr/include"
-I/usr/include
%
CFLAGSにはCコンパイラのオプションが収められていることが多い。それはOSが要求する基本的なオプションだったり、最適化オプションだったりとさまざまだ。
そのため、CFLAGSは上書きされずに、環境に用意されているCFLAGSに、新たに別のオプションを追加することで対象となるソフトウエアをビルドできるようにする、という使い方をされていることが多い。具体的には次のような感じだ。
CFLAGS+= -I/usr/local/include
CFLAGS+= -L/usr/local/lib
CFLAGS+= -g
test:
echo cc $(CFLAGS) -o obj.o src.c
C言語は一定のポータリビリティを持っているものの、ビルド方法がそのまま別のプラットフォームでも使えるわけではなく、ある程度環境に応じた書き換えを行う必要がある。そうした場合に、「+=」を使って必要となる値を追加する、みたいなことをやるわけだ。
実行すると次のようになる。
% export CFLAGS="-I/usr/include"
% /usr/bin/make
echo cc -I/usr/include -I/usr/local/include -L/usr/local/lib -g -o obj.o src.c
cc -I/usr/include -I/usr/local/include -L/usr/local/lib -g -o obj.o src.c
%
CFLAGSは、特にこういった使われ方をすることが多い。ほかにも似たような目的で使われる変数があり、プログラミングにおけるビルドの工程で使われることが多い。
本連載ではMakefileをプログラミング目的で使おうとはしていないので、この機能が必要になるケースはあまりない。ただし、多くのMakefileでこういった書き方がされているので、機能としては知っておこう。Makefileは、時として呪文めいた記述に見えることもあるが、実際にその根幹にあるルールは比較的シンプルである。把握さえしておけば、それほど苦労することなく理解可能なはずだ。