Makefile
概要
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 が実行される。clean や install は引数で指定する。
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) が実行される。CC、CFLAGS、CPPFLAGS は定義済みであり、上書きして処理を変更できる。
暗黙変数一覧
| 変数 | 説明 | 既定値 |
|---|---|---|
| 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 には文字列処理と条件分岐の関数がある。
テキスト変換関数
$(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++/gcc に MinGW、make に GnuWin、cmake に 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 の書き方をまとめた。備忘録として今後も更新していく。