Jaybanuan's Blog

どうせまた調べるハメになることをメモしていくブログ

Makefileで、実行するコマンドに環境変数を渡す方法

やりたいこと

makeで実行するコマンドに対して、つまりmakeから起動するサブプロセスに対して、Makefile内で定義した変数を環境変数として引き渡したい。 変数の引き渡しについて、makeのドキュメントにsub make(makeの再帰呼び出し)の説明があるが、実質的にはその説明内容はサブプロセスの起動にも適用できそう。

以降では、サブプロセスへの環境変数の渡し方ついて調査した結果を記しておく。

調査の前提

以下のMakefileをベースにして、必要に応じて編集して調査した。

FOO := foo
BAR := bar

.PHONY: test1
test1:
   @env | grep -E "(FOO|BAR)" || true

.PHONY: test2
test2:
   @env | grep -E "(FOO|BAR)" || true

このMakefileの冒頭では、変数FOOBARを定義している。 そしてターゲットtest1で、コマンドenvを実行することにより、環境変数FOOBARが含まれているかを確認している。 ターゲットtest2の内容はターゲットtest1と同じだが、環境変数のスコープを確認するために利用する。

現時点ではFOOBAR環境変数に含まれていないので、以下に示すようにmakeを実行しても何も表示されない。

$ make test1
(何も表示されない)

$ make test2
(何も表示されない)

ちなみに、Makefile内のコマンドラインの末尾の|| trueは、マッチする文字列がなかった場合にgrepがエラーを返してmakeが失敗に終わってしまうので、その対策である。

方法1) すべての変数を環境変数として引き渡す

makeのデフォルトの挙動では、Makefileで定義した変数はサブプロセスに引き渡されることはない。 しかしながらexportという命令を利用することで、デフォルトの挙動を変更して、Makefileで定義したすべての変数を環境変数としてサブプロセスに引き渡すことができる。

Makefileは以下のようになる。

export
FOO := foo
BAR := bar

.PHONY: test1
test1:
   @env | grep -E "(FOO|BAR)" || true

.PHONY: test2
test2:
   @env | grep -E "(FOO|BAR)" || true

このMakefileを利用した実行結果は以下のようになり、環境変数FOOBARがサブプロセスであるenvに引き渡されていることが分かる。

$ make test1
BAR=bar
FOO=foo

$ make test2
BAR=bar
FOO=foo

なお、今回は試していないが、同様の効果を得るために.EXPORT_ALL_VARIABLESという特別なターゲットも利用できるらしい。 これは、古いmakeを利用すると予約語exportが処理できずにエラーになるために、その回避策として利用するようである。

方法2) 特定の変数のみ環境変数として引き渡す

これはbash環境変数のやり方に似ている。 変数定義の先頭にexportを付与することで、Makefile中の変数を環境変数としてサブプロセスに引き渡すことができる。

Makefileは以下のようになる。

export FOO := foo
BAR := bar

.PHONY: test1
test1:
   @env | grep -E "(FOO|BAR)" || true

.PHONY: test2
test2:
   @env | grep -E "(FOO|BAR)" || true

このMakefileでは、変数FOOをサブプロセスに環境変数として引き渡すために、変数FOOの定義にexportを付与している。 一方で、変数BARにはexportを付与していないので、サブプロセスの環境変数には含まれない。

このMakefileを利用した実行結果は以下のようになり、環境変数FOOがサブプロセスであるenvに引き渡されていることが分かる。

$ make test1
FOO=foo

$ make test2
FOO=foo

方法3) 特定のターゲットのサブプロセスにのみ環境変数を引き渡す

target-specific variable(ターゲット固有の変数)を利用することで、特定のターゲットで起動されるサブプロセスに環境変数を引き渡すことができる。

Makefileは以下のようになる。

FOO := foo
BAR := bar

.PHONY: test1
test1: export FOO := $(FOO)
test1:
   @env | grep -E "(FOO|BAR)" || true

.PHONY: test2
test2:
   @env | grep -E "(FOO|BAR)" || true

このMakefileでは、ターゲットtest1で変数FOOをサブプロセスに環境変数として引き渡すために、次の1文を追加している。

test1: export FOO := $(FOO)

代入を省略してtest1: export FOOとできれば楽なのだが、残念ながらMakefileの文法上それはできないようだ。

このMakefileを利用した実行結果は以下のようになり、ターゲットtest1でのみ、環境変数FOOがサブプロセスであるenvに引き渡されていることが分かる。

$ make test1
FOO=foo

$ make test2
(何も表示されない)

方法4) 特定のコマンドに対して環境変数を引き渡す

ターゲットの中のコマンドラインで、コマンドの先頭に引き渡す環境変数を書いておく。 このやり方はmakeとは関係がなく、bashの仕様に沿ったもの。

Makefileは以下のようになる。

FOO := foo
BAR := bar

.PHONY: test1
test1:
   @FOO=$(FOO) env | grep -E "(FOO|BAR)" || true

.PHONY: test2
test2:
   @env | grep -E "(FOO|BAR)" || true

このMakefileを利用した実行結果は以下のようになり、コマンドライン環境変数を指定した場合のみ、環境変数FOOがサブプロセスであるenvに引き渡されていることが分かる。

$ make test1
FOO=foo

$ make test2
(何も表示されない)

おわりに

exportの他にも、unexportoverrideなどの命令を利用することで、環境変数の取扱をより詳細に制御できるのではと思うが、今回はそこまでは踏み込んでいない。 差し当たって、上記の4つの方法を使えれば、今のところ個人的には問題なさそう。