読者です 読者をやめる 読者になる 読者になる

GMake サバイバルガイド

make サバイバルガイド

この GMake サバイバルガイドでは、まけふぁいる*1をささっと書いて楽をしたいけど、
どーやって書くのかわからないといった様な人たちに向けてこんな感じに書けば良いんじゃないかなーというのを書いていると思います。

あと、この記事のまけふぁいるのサンプルコードは便宜上 4 半角スペースでインデントしていますが、まけふぁいるのインデントは絶対にタブなので、実際にやって見る場合は注意してください。

最新のソースコードを実行したい!

Crystal のソースコードコンパイルしてから実行するのは億劫ですよね。

% crystal build ./hoge.cr
% ./hoge
% crystal build ./hoge.cr
% ./hoge
% crystal build ./hoge.cr
% ./hoge
% crystal build ./hoge.cr
% ./hoge

何度も続けて行うなら、何とかして自動化してしまいたいもの。

まあ、cyrstal run ./hello.cr とかすれば Crystal のソースコードから実行できるんですが、それは無視します。

以下のようなへろーわーるどなソースコードがあったとします:

# hello.cr
puts "Hello, World!"

make ってやるだけで、実行ファイルがなかったり古かったりしたらコンパイルして とりあえず実行したい場合は私はまけふぁいるをこんな風に書きます:

.PHONY : run
run: hello
    ./hello

hello: hello.cr
    crystal build --release $^

.PHONY: run というのは run というシンボルはファイルじゃねーからよく覚えとけ!という意味を表明するために書いています。
これは省略可能です。

すぐ下の:

run: hello
    ./hello

こういうのをこの記事ではレシピ*2と呼びましょう。
レシピとはあるファイル○○を作るにはファイル△△が必要で、××するとできるというのをまけふぁいるの記述で書いたものです。
それをまけファイル用の記述で書くとこのようになります:

○○: △△
    ××

先程のレシピに戻ると run というファイルには hello というものが必要で、run を作るには ./hello が必要で、 hello コマンドを実行すればいいということが書いてあります。
しかし、run というファイルは実際には作成されないので、何度も実行することが可能です。

そういえば、hello はどうやって造られるのでしょう?
hello のレシピはすぐ下にあります:

hello: hello.cr
    crystal build --release $^

hello というファイルには hello.cr というファイルが必要で、crystal build --release $^ を実行すればいいよね!ということが書いてあります。
$^ は必要なファイル──この場合では hello.cr ですね──を参照する変数です。
作成するファイルを参照する変数は $@ なので、覚えておいてください。

とはいえ、この 2 つをもし忘れてもレシピは書けます。

hello: hello.cr
    crystal build --release hello.cr

という風に書けばいいだけだからです。
替わりにめんどくさいというだけです。

たくさんのソースコードの中で、新しいのだけコンパイルしたい!!

おーけーべいべ!
まさにそのために make は存在します。

例えば、ソースツリーに以下の LiveScript のソースコードがあったとします:

./routes.ls
./test/index.ls
./gulpfile.ls
./public/components/App.ls
./public/components/AddRoute.ls
./public/components/CounterRoute.ls
./public/store.ls
./public/action.ls
./server.ls
./config.ls

それに対して私の書いたまけふぁいるはこうです:

srcs = \
    gulpfile.js \
    routes.js \
    server.js \
    config.js \
    test/index.js \
    public/store.js \
    public/action.js \
    public/components/AddRoute.js \
    public/components/App.js \
    public/components/CounterRoute.js

.PHONY : build
build: $(srcs)

gulpfile.js: gulpfile.ls
    lsc --debug --no-header -b -c $^

routes.js: routes.ls
    lsc --debug --no-header -b -c $^

server.js: server.ls
    lsc --debug --no-header -b -c $^

config.js: config.ls
    lsc --debug --no-header -b -c $^

test/index.js: test/index.ls
    lsc --debug --no-header -b -c $^

public/store.js: public/store.ls
    lsc --debug --no-header -b -c $^

public/action.js: public/action.ls
    lsc --debug --no-header -b -c $^

public/components/AddRoute.js: public/components/AddRoute.ls
    lsc --debug --no-header -b -c $^

public/components/App.js: public/components/App.ls
    lsc --debug --no-header -b -c $^

public/components/CounterRoute.js: public/components/CounterRoute.ls
    lsc --debug --no-header -b -c $^

これでもまだ力技を使っていますが、わかりやすいかもしれませんね。

test/index.ls があるので、test はこのように書けるかもしれません:

test: test/index.ls
    mocha $^ --compilers ls:livescript

まけふぁいるでは、変数も使えます。

srcs = \
    gulpfile.js \
    routes.js \
    server.js \
    config.js \
    test/index.js \
    public/store.js \
    public/action.js \
    public/components/AddRoute.js \
    public/components/App.js \
    public/components/CounterRoute.js

が、その例です。
これは、○○や△△、××で使用できます*3
変数の値を参照するには $(src) のように、$() で変数名を囲みます。

変数を使わない場合、build はこのように、△△の部分が長くなってしまい、見るに絶えません。

build: gulpfile.js routes.js server.js config.js test/index.js public/store.js public/action.js public/components/AddRoute.js public/components/App.js public/components/CounterRoute.js

そういえば、build には××の部分が存在しないことに気づいていると思います。
build の××の部分は、既に△△のそれぞれの××が行っているので、必要ないのです。

(とはいえ、.o を使って実行ファイルや .dll.so を作成するような場合は必要です。)

今回は .ls.js にトランスパイルするだけなので、××は書いていません。

えーん、あるファイル一つだけ make したいよー

上の例で server.ls だけをビルドしたいとしましょう。
この場合は以下のように打つだけです:

% make server.js

public/components/CounterRoute.ls なら?

% make public/components/CounterRoute.js

ですね。

test/index.ls なら?

% make test/index.js

ですね。

make CounterRoute.jsmake App.js としないように気をつけてください。
それらは多分あなたが意図しているのとは違うファイルです。

そうそう、上の例で、make build と打つ必要はありません。
make と打つと、一番上のレシピ──この場合は build ──が実行されるからです。

いったんビルドしたのも全部ビルドしたいよ〜

make にオプション -B を付けてください。

% make -B

*1:Makefilemakefile ともいう。

*2:多分、ほんとは違う名前だと思う

*3:○○の部分には複数は指定できないかも?