[開発]GNU makeのMakefileに、シェルスクリプトを自然に書くたった一つの方法
たった一つとか嘘です。
ググッても中々出てこなくて、ちょっといじってたらできちゃったので書き残しておきます。
GNU makeでは1行毎に1つのシェルで実行されるので、
foo:: if true ; then echo "make love" ; fi
は
foo:: $(SHELL) -c 'if true ; then echo "make love" ; fi'
と同じような挙動になります。
よってシェルスクリプトが複数行に渡る場合、
foo:: if true then echo "make love" fi
なんて書いてしまうと、
foo:: $(SHELL) -c 'if true' $(SHELL) -c 'then' $(SHELL) -c ' echo "make love"' $(SHELL) -c 'fi'
という感じで実行されることになるので当然エラーになります。
そこで以下のような書き方が良く用いられています。
foo:: if true ;\ then \ echo "make love" ;\ fi
これ見た目上は複数行に分けて書けてはいますが、セミコロンの有無がワケワカランことになる上に、長くなってくると非常に猛烈に見辛くなります。しんどいです。
ここで、GNU makeでヒアドキュメントのようなことをする方法を紹介しておきます。
defineを使うと、通常の変数宣言とは違って改行を含めることができます。GNU make 日本語訳(Coop編) - 変数の利用法
define FOO a b c endef
ただこれをそのまま変数として
foo:: echo "$(FOO)"
のように使ってしまうと
foo:: echo "a b c "
と展開され、これまた各行が1行ずつシェルに実行されてしまうのでエラーになります。
そこで、変数をexportして環境変数として扱います。
export FOO foo:: echo "$${FOO}"
ここまでくれば後は簡単でしょう。
echoしたものをshに食わせれば良いだけです。
define MAKELOVE if true then echo "make love" fi endef export MAKELOVE foo:: echo "$${MAKELOVE}" | $(SHELL)あら?インタラクティブシェルじゃなくなってしまって困るぞ…。
foo:: echo "$${MAKELOVE}" > /tmp/$$$$ ; $(SHELL) /tmp/$$$$ ; rm -f /tmp/$$$$とりあえずこれでお茶を濁し中…
どや?
ちなみに、シェル変数は相変わらず$$varnameと$を重ねて書かないといけないので、完璧に自然なシェルスクリプトになるわけではありません。あしからず。
みんなもっとmake loveしようぜ。
make: don't know how to make love. Stop.