Makefile

Makefile は、UNIX 系 OS で一般的なビルドツールである make が実行する手順を記述したテキストファイルである。

概要

make は、開発者が作成したソースコードから最終的な実行ファイルを得るために、コンパイルやリンクなどの一連の処理を自動で実行するビルドツールである。Linux などの UNIX 系 OS で標準的に使われてきた。

開発者は実行したい手順を Makefile に記述する。make コマンドは内容を読み取り、コマンドを順番に実行してビルドを完了する。

Makefile とは

Makefile にはコンパイラ、ソースファイル、生成物、ビルド順序、依存関係などを記述する。make コマンドを実行するだけでビルドでき、内容を読めばプログラムのビルド方法も把握できる。また make は更新が必要なファイルを自動判定し、コンパイル回数を最小限に抑える。

GNU Make のホームページでは Make を次のように説明している。

  • GNU Make is a tool which controls the generation of executables and other non-source files of a program from the program’s source files.

これにより、次の利点が得られる。

  • エンドユーザーは詳細な手順を知らなくてもパッケージをビルドしてインストールできる。
  • 変更されたソースファイルから更新が必要なファイルを判断し、適切な順序を自動決定する。
  • Make は C 言語以外のコンパイル言語にも対応する。

Make - GNU Project - Free Software Foundation

Makefile の基本的な書き方

多くのライブラリを使う C/C++ プロジェクトのビルドでは make が便利である。

基本構文は Target: SourceFile と、先頭にタブを付けたコマンドの 2 行で構成される。改行をセミコロンで置き換えて 1 行にまとめることもできる。また 変数名=値 の形式で変数を定義できる。

Target : [Prerequisites]
	Recipe
  • Target: プログラムによって生成されるファイル名。
  • Prerequisites: Target の生成に使う入力ファイル。複数指定または省略が可能。
  • Recipe: make が実行する処理。\ で複数行に分割でき、各行の先頭にはタブが必要。
hello: hello.c
	gcc -o hello hello.c

生成ファイルを削除またはコピーするコマンドも追加できる。

clean:
	rm -f *~ hello 
install: hello 
	install -s hello.exe Path

引数なしで make を実行すると、最初の Target が実行される。cleaninstall は引数で指定する。

make clean
make install

Makefile 以外のファイルを指定する場合は -f を使う。

make -f sample.mk

変数も利用できる。

CXX            = g++
OPTIMIZE       = -O3
CFLAGS         = -IC:/Users/include \
	-IC:/Python/include 
DEST           = C:/Users/Local
LDFLAGS        = -LC:/Users/Local/libs
LIBS           = -lpython
OBJS           = hello

all: clean $(PROGRAM) install
$(PROGRAM): $(OBJS)
	$(CXX) -o $(OBJS) $(OBJS).cpp $(CFLAGS) $(LDFLAGS) $(LIBS)
clean: rm -f *~ $(OBJS)
install: $(PROGRAM)
	install -s $(OBJS).exe $(DEST)

Makefile の変数

Makefile の変数には、暗黙変数と独自に定義する変数がある。暗黙変数とは、暗黙ルールで使用される定義済み変数である。

暗黙ルールとは

一部のルールと変数は事前定義されている。たとえば C ソースファイルのコンパイルでは $(CC) -c $(CFLAGS) $(CPPFLAGS) が実行される。CCCFLAGSCPPFLAGS は定義済みであり、上書きして処理を変更できる。

暗黙変数一覧

変数 説明 既定値
AR アーカイブ保守プログラム ar
AS アセンブリ実行プログラム as
CC C コンパイラ cc
CXX C++ コンパイラ g++
CO RCS 取得プログラム co
CPP C プリプロセッサ $(CC) -E
FC Fortran と Ratfor のコンパイラまたはプリプロセッサ f77
GET SCCS 取得プログラム get
LEX Lex 文法を C または Ratfor に変換 lex
PC Pascal コンパイラ pc
YACC Yacc 文法を C または Ratfor に変換 yacc
YACCR Yacc 文法を Ratfor に変換 yacc -r
MAKEINFO Texinfo ソースを Info に変換 makeinfo
TEX TeX ソースから TeX DVI を生成 tex
TEXI2DVI Texinfo ソースから TeX DVI を生成 texi2dvi
WEAVE Web を TeX に変換 weave
CWEAVE C Web を TeX に変換 cweave
TANGLE Web を Pascal に変換 tangle
CTANGLE C Web を C に変換 ctangle
RM ファイル削除コマンド rm -f
ARFLAGS アーカイブ保守プログラムのフラグ rv
ASFLAGS アセンブラのフラグ なし
CFLAGS C コンパイラのフラグ なし
CXXFLAGS C++ コンパイラのフラグ なし
COFLAGS RCS co のフラグ なし
CPPFLAGS C プリプロセッサとコンパイラのフラグ なし
FFLAG Fortran コンパイラのフラグ なし
GFLAG SCCS get のフラグ なし
LDFLAGS リンカ呼び出し時にコンパイラへ渡すフラグ なし
LFLAGS Lex のフラグ なし
PFLAGS Pascal コンパイラのフラグ なし
RFLAGS Ratfor 用 Fortran コンパイラのフラグ なし
YFLAGS Yacc のフラグ なし

https://www.gnu.org/software/make/manual/make.html#Implicit-Variables

変数の定義

記号 詳細
= 再帰的に展開される変数
:= 単純に展開される変数
foo = $(bar)
bar = $(ugh)
ugh = Huh?

all:
  echo $(foo) # => Huh?

変数の参照

$(name) または ${name} を使う。

objects = program.o foo.o utils.o
program : $(objects)
	cc -o program $(objects) 

$(objects) : defs.h

ルール内で使用する自動変数

自動変数を使うと、Target と Prerequisites を短く参照できる。

変数 機能
$@ Target 名
$< 最初の Prerequisite 名
$^ 重複を除いたすべての Prerequisite 名
$? Target より新しい Prerequisite 名
$+ 重複を含むすべての Prerequisite 名
$* Target のパターンに一致した部分

$@

aaa/bbb/foo:
	echo $@     # => aaa/bbb/foo
	echo $(@D)  # => aaa/bbb
	echo $(@F)  # => foo

$<

output/foo: input/bar input/baz
	echo $<     # => input/bar
	echo $(<D)  # => input
	echo $(<F)  # => bar

$^

output/foo: input/bar input/baz
	echo $^     # => input/bar input/baz
	echo $(^D)  # => input input
	echo $(^F)  # => bar baz

$?

output/foo: input/bar input/baz
	echo $?     # => input/bar
	echo $(?D)  # => input
	echo $(?F)  # => bar

$+

重複を含むすべての Prerequisite 名を表す。通常は $^ がよく使われる。

output/foo: input/baz input/baz input/baz
	echo $+     # => input/baz input/baz input/baz
	echo $(+D)  # => input input input
	echo $(+F)  # => baz baz baz

$*

Target のパターンに一致した部分を表す。関連ファイルを生成するときに便利である。

Makefile の関数

Make には文字列処理と条件分岐の関数がある。

Functions (GNU make)

テキスト変換関数

$(function arguments) または ${function arguments} で呼び出す。

参考: https://www.gnu.org/software/make/manual/make.html#Text-Functions

shell

シェルコマンドを実行する。

files := $(shell echo *.c)
INCLUDE := $(shell find $(INCDIRS) -type d)
SRCDIR = ./srcs
SRCS := $(shell find $(SRCDIR) -name *.c)
all:
	echo $(files) # => hoge.c foo.c
	echo $(INCLUDE) # => include
	echo $(SRCDIR) # => ./srcs
	echo $(SRCS) # => hoge.c foo.c
RESULT = $(shell seq 1 10)
all:
	echo $(RESULT)  # => 1 2 3 4 5 6 7 8 9 10

addprefix

接頭辞を追加する。-l などのオプションに便利である。

FILES := foo bar
all:
	echo $(addprefix src/,$(FILES))  # => src/foo src/bar

dir

ファイル名からディレクトリ部分を取得する。

FILES := src/hoge.c src/hoge.h index.html
all:
	echo $(dir $(FILES))  # => src/ src/ ./

notdir

ファイル名を取得する。

FILES := ./dir/hoge.txt
all:
	echo $(notdir $(FILES))  # => hoge.txt

置換参照

変数値の一致する部分を置換する。patsubst 関数の短縮形である。

foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
bar2 := $(patsubst %.o,%.c,$(foo))

realpath、suffix、wildcard

"$(realpath ./dir/hoge.txt)"//C:users/hoge/bin/hoge.txt
"$(suffix ./dir/hoge.txt)"//.txt
"$(wildcard ./dir/*.txt)"//hoge.txt hogehoge.txt

条件分岐

"$(if $(VAR1),$(exist),$(none))"

ifeq ($(CC),gcc)
  libs=$(libs_for_gcc)
else
  libs=$(normal_libs)
endif

Makefile のデバッグ方法

GNU Make の -n オプションは、コマンドを実行せずに表示する。

CC = gcc
OBJ = hoge.c
CFLAGS = -c $(OBJ)
$ make -n
gcc -c hoge.c -o hoge

warning 関数は make 実行時に内容を出力する。

$(warning MAKE = $(MAKE))
$(warning CC = $(CC))
$(warning CFLAGS = $(CFLAGS))

g++、make、cmake のインストール方法

Windows でのインストール

Windows では g++/gccMinGWmakeGnuWincmake に CMake installer を利用できる。

コマンドラインからインストールする場合:

choco install mingw
choco install make

環境変数にパスを設定する。

C:\Program Files (x86)\MinGW\bin
C:\Program Files (x86)\GnuWin32\bin

Linux でのインストール

一括でインストールする。

sudo apt install build-essential

個別にインストールする。

sudo apt install g++
sudo apt install make

まとめ

Makefile の書き方をまとめた。備忘録として今後も更新していく。