2013年1月18日金曜日

make のお話

山本です。

今回は古くて旧い make を取り上げてみます。
昔と違って、今ならビルド自動化ツールの選択肢として rake も ant も grunt も xbuild もありますが、それでも敢えて make です。

make とは

make とは、あらかじめ Makefile に生成物 とその材料との間の依存関係を書いておくことで、生成物を得るのに必要十分なプロセスを自動的に行うツールです。
特に、中間生成物が存在するケースで再生成漏れを防ぐのに有用です。
make の特徴
  • 基本的に、何かを生成するために外部のプログラム(コンパイラ etc.)を起動する。シェルスクリプトに思想が似ています。
  • 自力でソースを解析してまで依存関係を判断することはない。ただし、外部ツール(mkdep 等)の支援のもとで依存関係の記述を自動化させる事例はあります。
make の種類
make には、主に2種類のメジャーな方言があります。 基本的な記述方法は一緒ですが、条件分岐やinclude等の特殊な構文や、変数展開時に使えるオプション指定に違いがみられます。
  • GNU make
  • BSD make (a.k.a. pmake)

make 基礎篇

依存関係(ルール)
ターゲット:依存物
        レシピ
ターゲット、依存物ともに複数個指定ができます。(その場合はスペース区切り)
レシピは行頭にタブ文字。複数行指定ができます。行頭にタブ文字以外が出現すると、そこでルールの記述が終わります。
 ターゲット以外はいずれも省略可能。
レシピを省略した場合は、そのターゲットが指定された依存物に依存している事実だけが記述されたことになり、 具体的なレシピは他の記述か拡張子をベースにしたデフォルト(推論規則)になります。
変数展開
$V
もしくは
${VARIABLE}
ただし、{}で括らない書式は、変数名が1文字な場合に限られます。
変数定義
普通に代入する
VAR = the value to be set
 行頭に空白は書けません。そのかわり、等号の前後に空白文字を入れることは可能です。
なお、変数に別の変数の値を代入することもできますが、代入された変数の値が実際に展開されるのは後になります。したがって、
A=$B
B=hoge
dummy:
        echo $A
は、 hoge が出力されます。
その場で変数展開したものを代入する
前述した挙動が望ましくなく、その時点での値を代入したい場合は、
A:=$B
とします。
未定義な場合に代入する
A ?= some default value
とすると、コマンドライン(後述)や環境変数などで値が与えられていない場合に限って代入が行われます。
追加する
 A+= append to A
とすると、変数の値に追加することができます。例えば、
A= Unique
A+= Vision
とした場合、 $A は Unique Vision と展開されます。(Unique と Vision の間に空白が入っている点に注目)
コマンドラインから
make VAR=value
のように、コマンドラインから変数を指定することもできます。

make 応用篇

記述量を減らそう!
自動変数
make が勝手に設定してくれる変数がいくつかあります。
主なものを挙げてみます。
  • $< 依存物
  • $@ 生成物
注意点としては、これらの変数はレシピ実行中に今まさに生成しようとしているターゲットに関する値として展開されます。
つまり、中間生成物の生成レシピを実行している最中では、それぞれ中間生成物の依存物と中間生成物そのものの名前に展開されるということです。
パターンによるルール
all: foo.css bar.css
foo.css: foo.styl
        @stylus foo.styl
bar.css: bar.styl
        @stylus bar.styl
と書く代わりに、
all: foo.css bar.css
%.css: %.styl
        @stylus $<
と書けます。($< は依存物を表す自動変数)
変数の展開時パターン置換
SRCS= foo.styl bar.styl
と書いてあるとすると、
${SRCS:%.styl=%.css}

foo.css bar.css
に展開されます。

共通部分の別ファイルへの切り出し
変数置換を駆使するなどして、ルールから具体的な値が排除できたら、ルールを別ファイルに切り出して、本体 Makefile から include させることで Makefile をまたいだ共通化が図れます。
all: foo.css bar.css
%.css: %.styl
        @stylus $<
の例を取り上げると、
  • Makefile:
    STYLES= foo.styl bar.styl
    include stylus-common.mk
  • stylus-common.mk:
    all: ${STYLES:%.styl=%.css}
    %.css: %.styl
            @stylus $<
のような2ファイルに分割できます。(GNU make の場合)
この例ではあまりありがたみはありませんが、もう少し複雑なルール集合を、いろいろな Makefile で使いたくなってきた時に威力を発揮します。
(なお、 BSD make 系では、 include filename.mk の代わりに .include "filename.mk" などとします)
風変わりなレシピ
常に失敗するレシピ
例えば、仕様書が更新されたらテストを更新しなければいけないことを明確化することを考えましょう。
具体的にテストをコマンドで更新することは一般的には不可能ですが、更新の必要があることを開発者が知る必要もあるわけです。
その場合、
testcase.js: specification.doc
        @echo 仕様が更新されています。テストケースも更新しましょう。 >&2
        @false
 のように、最後に false を実行させることで無理やり失敗させる手が有効です。

0 件のコメント:

コメントを投稿