Makefileで外部プログラムの実行結果を変数に取り込む

Makefileで外部のプログラムやコマンドを実行した結果を変数に取り込む方法を調べて結構苦労したのでメモとして残しておく。
結論としては\\$(shell  ), \` \`とあと \\$(eval  )あたりを駆使する。
### 簡単な例
例としてdate +%M:%S:%Nで時刻を取得してそれを変数VALに取り込む例を考える。

```makefile:例１
VAL := $(shell date +%M:%S:%N)
run :
    @echo $(VAL)
```
```makefile:例２
VAL := `date +%M:%S:%N`
run :
    @echo $(VAL)
```
```:結果１
> make run
52:14:280298700
```
```:結果２
> make run
52:59:203304400
```

このように\\$(shell  )か\` \`のどちらかを使えば良いだけである。これだけだと大した話ではないのだが、状況が少し込み入って来るといろいろと面倒にな事がある。

### 変数の受け渡しと複数回の参照
まずMakefile内で設定した変数を外部プログラムに環境変数として渡したい場合を考える。ここでは渡したい変数としてMSGを設定する。環境変数として外部に渡す為にMSGの前にexportを付ける。またMakefileのrun内のechoでのVALの表示回数を3回に増やす。実行する外部プログラムは以下の様にやはり現在時刻の取得としてその後ろに\\$MSGの値を付けたシェルスクリプトにする。

```sh:./now.sh
#!/bin/sh
TIME=`date +%M:%S:%N`
echo "$TIME : $MSG"
```
```makefile:例３
export MSG := Hello!
VAL := $(shell ./now.sh)
run :
    @echo $(VAL)
    @echo $(VAL)
    @echo $(VAL)
```
```makefile:例４
export MSG := Hello!
VAL := `./now.sh`
run :
    @echo $(VAL)
    @echo $(VAL)
    @echo $(VAL)
```
これらを実行するとどうなるか。。。

```:結果３
> make run
26:32:118294900 :
26:32:118294900 :
26:32:118294900 :
```
```:結果４
> make run
26:27:190915200 : Hello!
26:27:223885900 : Hello!
26:27:256983200 : Hello!
```
このようにまず、\\$(shell  )を使った結果３ではexportを使っているのに\\$MSGが環境変数として./now.shに渡っていない。\` \`を使用した結果４では\\$MSGはきちんと渡っているが、echo \\$VALでVALの値を表示する度に./now.shが実行されて違う値となってしまう。
まとめるとそれぞれ以下の欠点があると言える。


- ***\\$(shell)ではMakefile内でexportで設定した変数が渡らない。***
- ***\` \`はコマンドそのものがマクロとして代入され参照される度にそのコマンドが実行されてしまう。***

以下にそれぞれ解決策を提示する。

### 解決策
- \\$(shell)内で環境変数の設定をするshシェルコマンドを追加。

```makefile:例３）解決策１【exportで環境変数を設定してから実行】
MSG := Hello!
VAL := $(shell export MSG=$(MSG); ./now.sh)
run :
    @echo $(VAL)
    @echo $(VAL)
    @echo $(VAL)
```
```makefile:例３）解決策２【envで環境変数を指定して実行】
MSG := Hello!
VAL := $(shell env MSG=$(MSG) ./now.sh)
run :
    @echo $(VAL)
    @echo $(VAL)
    @echo $(VAL)
```

- いったん結果をファイルにダンプしてからcatでその内容を表示する。

```makefile:例４）解決策１【catコマンドのマクロを設定】
export MSG := Hello!
VAL := `cat dump.txt`
run :
    ./now.sh > dump.txt
    @echo $(VAL)
    @echo $(VAL)
    @echo $(VAL)
```
```makefile:例４）解決策２【run内でcatコマンドのマクロを設定】
export MSG := Hello!
run :
    ./now.sh > dulmlp.txt
    $(eval VAL := `cat dump.txt`)
    @echo $(VAL)
    @echo $(VAL)
    @echo $(VAL)
```
```makefile:例４）解決策３【\$(shell)内のcatでファイルの内容を取得】
export MSG := Hello!
run :
    ./now.sh > dump.txt
    $(eval VAL := $(shell cat dump.txt))
    @echo $(VAL)
    @echo $(VAL)
    @echo $(VAL)
```
```:結果３
> make run
42:37:754889100 : Hello!
42:37:754889100 : Hello!
42:37:754889100 : Hello!
```
```:結果４
> make run
42:42:755746800 : Hello!
42:42:755746800 : Hello!
42:42:755746800 : Hello!
```
どれもきちんとMSGも表示されかつVALの値も同じとなった。

例４の解決策は値のファイルへのダンプおよびVALへの値の取得を別ルールとして分離すると分かりやすくて良いかもしれない。

```makefile:例４）解決策【VAL取得の分離】
export MSG := Hello!
#VAL := `cat dump.txt`
run : get_VAL
    @echo $(VAL)
    @echo $(VAL)
    @echo $(VAL)
get_VAL :
    ./now.sh > dump.txt
    $(eval VAL := $(shell cat dump.txt))
#   $(eval VAL := `cat dump.txt`)
```
又、以下も可能であった。

```makefile:例４）解決策【最初にファイルダンプ】
export MSG := Hello!
DUMP := `./now.sh > dump.txt`
VAL := $(shell cat dump.txt)
run : 
    @echo $(VAL)
    @echo $(VAL)
    @echo $(VAL)
```

尚、ファイルにダンプせずできないものかと以下試してみたがうまくは行かなかった。

```makefile:例４）失敗策【ファイルダンプせず値の取得】
export MSG := Hello!
NOW := `./now.sh`
VAL := $(shell echo $(NOW))
run :
    @echo $(VAL)
    @echo $(VAL)
    @echo $(VAL)
```
```:結果４
> make run
04:25:965031000 :
04:25:965031000 :
04:25:965031000 :
```
実行は一回だが\$(shell)内での実行とみなされやはりMSGが渡っていない。

\\$(shell export)を使ったやり方の方が優れている気もするがそうとも言えない。渡す変数が大量になってくると書くのが大変になって入れ忘れなどのミスを誘発しやすくなる。その場合は\` \`を使った方法にした方が良いだろう。\` \`の方はどうしてもファイルダンプしなくてはならない。それが難しい場合は欠点となる。
いずれにせよ、上記のような込み入った状況以外の単純な状況ではどちらでも大差はない。

###まとめ
|         |  変数への代入方法           　|変数の受け渡し                               |
|:-------:|:--------------------------|:---------------------------------|
|\$(shell)|コマンドの実行結果の値を代入。    |自動では環境変数として渡されない。シェルコマンドで明示的に記述する。 |
|\` \`    |コマンド自体をマクロとして代入。　　　　|Makefileのexportで環境変数として自動的に渡される。                      |
|\$(eval)  |実行部分で変数を動的に設定。    |  ー                              |

###参考記事
[makeでシェルの結果を利用する](http://ksaito11.hatenablog.com/entry/20100205/1265378341)
[Qiita : GNU make変数の伝播についてまとめ](https://qiita.com/aosho235/items/8cd094a2abd22bc62af0#%E3%81%BE%E3%81%A8%E3%82%81)
[Qiita : Makefileで実行時に変数代入したい](https://qiita.com/suin/items/220f6b574b88c8929379)
[Qiita : makefile 動的に変数に代入 共通して使う](https://qiita.com/cympfh/items/154a07552b84d2fb79ff)
[Qiita : シェル変数と環境変数の違いをコマンドラインで確認する](https://qiita.com/kure/items/f76d8242b97280a247a1)
[Qiita : 今さら聞けない！環境変数とシェル変数の違いと定義方法](https://qiita.com/pink/items/73e712345cfabaef31e8)
[Qiita : Linux 環境変数有効範囲](https://qiita.com/medaka5/items/9fbd751e20f8a026211b)
[【 env 】コマンド――環境変数を指定してコマンドを実行する](https://www.atmarkit.co.jp/ait/articles/1801/18/news016.html)
[【 export 】コマンド――環境変数やシェル変数を設定する](https://www.atmarkit.co.jp/ait/articles/1801/12/news015.html)