GNU Markfile の基本的な記法を調べる

GNU Makefile の基本的な書き方を確認します。

GNU Make

公式のマニュアル。

GNU Make Manual

2005 年に出版されているオライリーの書籍が無料公開されています。大感謝。凝ったことをするつもりはないので、とりあえず第 3 章あたりまで読んでみました。

O'Reilly GNU Make 第 3 版

環境

基本的な記法

Makefile というファイル名で、以下のシンタックスで情報を記述したファイルを作ります。

(作成ファイル): (依存関係のあるファイル)
    (実行するコマンド)

4.2 Rule Syntax

Makefile の例です。

target.txt: require.txt
    echo "Create a target.txt."
    touch target.txt

実行してみます。

$ # 依存関係のファイルがない状態で実行すると怒られる
$ make target.txt
make: *** No rule to make target 'require.txt', needed by 'target.txt'.  Stop.
$ # 依存関係のファイルを用意した後に実行すると、正しくコマンドが流れる
$ touch require.txt
$ make target.txt
echo "Create a target.txt."
Create a target.txt.
touch target.txt
$ # もう1度実行すると、作成するファイルが最新状態のためコマンドは流れない
$ make target.txt
make: 'target.txt' is up to date.
$ # 依存関係のファイルを再作成した後に実行すると、コマンドが流れる
$ touch require.txt
$ make target.txt
echo "Create a target.txt."
Create a target.txt.
touch target.txt

@ マーク

実行するコマンドの冒頭に @ をつけると、実行コマンドが表示されなくなります。

target.txt: require.txt
    @echo "Create a target.txt."
    touch target.txt

実行すると、echo 文の表示されなくなります。

$ make target.txt
Create a target.txt.
touch target.txt

.PHONY マーク

.PHONY マーク を利用することで、具体的なファイル名ではなく、擬似的な名前でルールを記述できるようになります。

4.6 Phony Targets

以下は run1run2 という疑似ターゲットを利用している例です。 run1run2 といったファイルの管理が不要になります。

.PHONY: run1
run1:
    @echo "run1 executed."

.PHONY: run2
run2: run1
    @echo "run2 executed."

実行してみます。

$ make run1
run1 executed.
$ # 疑似ターゲットは常に最新でないと判断され、その依存されているファイルも含めてかならず再構築されます。
$ make run2
run1 executed.
run2 executed.

その他の特殊ターゲットたちです。

4.9 Special Built-in Target Names

make コマンドを再帰的に実行

依存関係で管理するのではなく、make コマンドを再帰的に実行したい場合の例です。

5.7 Recursive Use of make

.PHONY: run3
run3:
    $(MAKE) run2
    @echo "run3 executed."

実行してみます。

$ make run3
make run2
make[1]: Entering directory '/home/zunda/work/make'
run1 executed.
run2 executed.
make[1]: Leaving directory '/home/zunda/work/make'
run3 executed.

変数

Makefile での変数の宣言は、以下の方法があります。

種類 記法 説明
単純展開変数 := Makefike を読み込んだ時に変数を評価
再帰展開変数(遅延評価変数) = 変数を利用する度に変数を評価
条件付き展開変数 ?= その変数が宣言されていない場合に評価

Makefile の例です。$@ は用意されている自動変数(予約変数)となり、ターゲット名の値を持っています。

VAR1 := "apple"
VAR2 := "banana"
VAR2 ?= "carrot"

.PHONY: run4
run4:
    @echo "VAR1: $(VAR1)"
    @echo "VAR2: $(VAR2)"
    @echo "Automatic Variables: $(@)"

実行してみます。

$ make run4
VAR1: apple
VAR2: banana
Automatic Variables: run4

自動変数(予約変数)の一覧は下記です。

10.5.3 Automatic Variables

VPATH

デフォルトでは、Make はカレントディレクトリにあるファイルを探しに行きますが、VPATH に Path の値をを追加することで、捜査先のディレクトリを追加することができます。

4.5 Searching Directories for Prerequisites

以下は ./subdir という Path を追加した Makefile 例です。

VPATH := src:./subdir

run5: require.txt subdir.txt
    @echo $(?)

実行してみます。subdir ディレクトリ下にあるファイルも見つけてくれています。

$ make run5
require.txt ./subdir/subdir.txt

シェルスクリプトの呼び出し

シェルスクリプトを呼び出してみます。

以下のコマンドライン引数と変数を扱うスクリプトを用意します。ファイル名は test1.sh

#!/bin/bash

echo "Hello."
# コマンドライン引数を表示
echo "\$1: ${1}"
# 変数 VAR4 を表示
echo "VAR4: ${VAR4}"

Makefile の記述です。

VAR3 := "AAA"

.PHONY: run6
run6:
    @export VAR4="BBB"
    @sh test1.sh $(VAR3)
    @echo "============="
    @export VAR4="BBB"; \
    sh test1.sh $(VAR3);

実行してみます。Makefile は、実行するコマンドを 1 行ずつ別プロセスを起動するため、同一のコマンドラインで実行するとシェルスクリプト内の変数をそのまま操作できるようになります。

$ make run6
Hello.
$1: AAA
BBB:
=============
Hello.
$1: AAA
BBB: bbb

シェルスクリプトの呼び出し(read コマンド)

以下のような read コマンドを利用してシェルスクリプトも扱えます。ファイル名は test2.sh

#!/bin/bash

read -p "Are you deploying the production environment? (Y, N): " INPUT
if [ ${INPUT} = "Y" ]; then
    echo "true"
else
    echo "false"
fi

Makefile です。

.PHONY: run7
run7:
    @sh test2.sh

ちゃんと標準入力を扱ってくれます。

$ make run7
Are you deploying the production environment? (Y, N): Y
true

コマンドオプション

make コマンドのオプションは man で確認できます。

man - make

ドライランの実行。

$ make run1 --dry-run
echo "run1 executed."

デバックの表示。

$ make run1 --debug=v --debug=j
GNU Make 4.3
Built for x86_64-pc-linux-gnu
Copyright (C) 1988-2020 Free Software Foundation, Inc.
License GPLv3+: GNU GPL version 3 or later <http://gnu.org/licenses/gpl.html>
This is free software: you are free to change and redistribute it.
There is NO WARRANTY, to the extent permitted by law.
Reading makefiles...
Reading makefile 'Makefile'...
Updating makefiles....
Updating goal targets....
Considering target file 'run1'.
File 'run1' does not exist.
Finished prerequisites of target file 'run1'.
Must remake target 'run1'.
Putting child 0x55e543240260 (run1) PID 8363 on the chain.
Live child 0x55e543240260 (run1) PID 8363
run1 executed.
Reaping winning child 0x55e543240260 PID 8363
Removing child 0x55e543240260 PID 8363 from chain.
Successfully remade target file 'run1'.