Makefile 作成ガイド

Makefile とは

コンパイルを楽にするもの。
どのファイルからどうやって何のファイルを作り出すのか書いておく。

再コンパイルが必要かどうかの判断も make が行い、必要であれば再コンパイルしてくれる。

Makefile の基本構成

Makefile は次のルールで構成する。

ターゲット: 依存関係
        レシピ
  • ターゲット:
    生成するファイルの名前や実行するアクションの名前

  • 依存関係:
    ターゲットを作るための入力ファイル
    複数書くときはスペースで区切る。
    一つも書かなくても良い。そのときは単純にレシピが実行されるだけ。

  • レシピ:
    make が実行するアクション
    レシピのインデントはタブにすること。
    空白にするとエラー「分離記号を欠いています (8 個の空白でしたが, TAB のつもりでしたか?). 中止.」になる。

make の実行

make コマンドで実行する。

Makefile もターゲットも指定しない場合、make はカレントディレクトリの Makefile に最初に現れるターゲットを実行する。
(最初に現れるターゲットをデフォルトターゲットと呼ぶ)

$ make

ターゲットを指定するには次のようにする。

$ make build

Makefile を指定するには -f オプションで指定する。

$ make -f /path/to/custom.mk

Makefile および実行方法の例

次の Makefile は main.c と utils.c から myprogram を作る。
これを Makefile という名前で保存する。

all: main.o utils.o
        gcc -o myprogram main.o utils.o

main.o: main.c
        gcc -c main.c

utils.o: utils.c
        gcc -c utils.c

デフォルトターゲットを実行する場合

デフォルトターゲット(all ターゲット)を実行する例

$ make

all ターゲットは以下の挙動をする。

依存関係(main.o と utils.o)が存在し、最新であるか確認する。

最新である場合、all ターゲットのレシピを実行して実行ファイルを作る。

そうでない場合、依存関係に対応するターゲットを実行する。
(main.o なら main.o ターゲット、utils.o なら utils.o ターゲット)

依存関係が準備できたら、all ターゲットのレシピを実行して実行ファイルを作る。

特定のターゲットを実行する場合

main.o ターゲットを実行する例

$ make main.o

main.o ファイルがない、または古ければファイルが再作成される。
再作成する必要がなければ「make: 'main.o' は更新済みです.」のように出力される。

make: '〈ターゲット名〉' に対して行うべき事はありません.

Makefile が間違っているのかと思ったがそうではなかった。
すべての依存関係が最新であり再ビルドの必要がない場合に、このようなメッセージが表示されるらしい。

それでも再ビルドしたいときは依存ファイルを消せば良い。

変数

次の形式で宣言する。

VAR_NAME = value

変数名は決まっているわけではないが、大文字のスネークケースが一般的らしい。

例)CC, CFLAGS, SOURCE_FILES

変数への値の代入には、いくつかの方法がある。

  • =: 遅延評価。変数が参照されるときに評価される。

  • :=: 即時評価。代入時に評価される。値は固定され、再評価はされない。

  • ?=: 条件付き代入。変数が未定義の場合にのみ、値を代入する。

  • +=: 追加代入。既存の変数に値を追加する。

VAR_NAME = value
VAR_NAME := value
VAR_NAME ?= value
VAR_NAME += value

基本的には即時評価で、必要なものだけ遅延評価とすれば良いかと。

自動変数

自動変数の一例。

  • $@: ターゲットファイル名を表す。

  • $*: サフィックス(拡張子)を除いたターゲットファイル名を表す。

  • $^: すべての依存ファイルのリストを表す。重複は除かれる。

  • $<: 最初の依存ファイルの名前を表す。

レシピの各コマンドはサブシェルで実行される

レシピ内の各コマンドは、それぞれ別のシェル環境(=サブシェル)で実行される。

たとえば次の example ターゲットを実行した場合、cd /tmp の結果は次の pwd には影響しない。
pwd は /tmp ではなく元のディレクトリを表示する。

example:
        cd /tmp
        pwd

特定のターゲットが実行されたときに変数を定義する

たとえば compile ターゲットが実行されたとき、変数 OBJS を定義するには次のように書く。

SRC := file1.c file2.c file3.c

# (1)
compile: OBJS = $(SRC:.c=.o)
# (2)
compile:
        $(MAKE) $(OBJS)

%.o: %.c
        gcc -c $< -o $@

この Makefile で make compile を実行すると、次のように動作する。

まずは(1)の部分で、変数 OBJS に「file1.o file2.o file3.o」が設定される。
続けて(2)の部分で、設定した変数 OBJS を使って再度 make が実行される。
以下のようになるイメージ。

$(MAKE) $(OBJS)
↓
make file1.o file2.o file3.o


同じターゲットを2回書くのは面倒なので、次のようにレシピの部分で変数を定義するようにしてみた。
がうまくいかなかった。

# Note: うまく動かない書き方
compile:
        OBJS = $(SRC:.c=.o)
        $(MAKE) $(OBJS)

エラーとして「make: OBJS: そのようなファイルやディレクトリはありません」が出力される。
どうも変数が $(MAKE) に引き継がれていないようだった。

原因は「OBJS = $(SRC:.c=.o)」と「$(MAKE) $(OBJS)」が別のプロセスで動くからだった。
〝レシピの各コマンドはサブシェルで実行される〟というのが Makefile の挙動なので、これはどうしようもない。
黙って依存関係で変数を定義するのが良いだろう。

パターンルールと静的パターンルール

  • パターンルール

  • 静的パターンルール

パターンルール

特定のパターンに一致するターゲットに対して適用されるルールのこと。
パターンルールでは % というワイルドカードを使って、ターゲット名の一部を抽象化する。

例)任意の .c ファイルに対応する .o ファイルを生成するパターンルール

%.o: %.c
    gcc -c $< -o $@
  • $< は最初の依存関係を表す(例の場合だと .c ファイル)

  • $@ はターゲットを表す(例の場合だと .o ファイル)

静的パターンルール

特定のターゲットリストに対してパターンルールを適用する方法のこと。
静的パターンルールでは、ターゲットリストとパターンを明示的に指定する。

例)foo.c から foo.o を、bar.c から foo.o を生成する静的パターンルール

objects = foo.o bar.o

$(objects): %.o: %.c
    gcc -c $< -o $@

サフィックスルールではなくパターンルールを使う

サフィックスルールは make の暗黙のルールを定義する古い方法である。
パターンルールの方が一般的で明確であるため、サフィックスルールは廃止されている。
(古い makefile との互換性のためにサポートはされているが)

Suffix Rules (GNU make)

いいなと思ったら応援しよう!