Help us understand the problem. What is going on with this article?

Makefileの関数

More than 3 years have passed since last update.

自分用にずっとまとめようと思って、下書き保存して温めていたMakefile関連です。
C++用のビルドからAndroid用のビルドまでMakefileを大活用しているが、
使う機会が少ないのでMakefileの関数はどうも慣れない&上手く活用できない。
そして毎回調べる。

ということで、
実用的なものから、今後使うことはないだろう的なものまで、
リファクタをする時のために調べた結果をまとめておきたい。

英語が読めない自分が英語のドキュメントを読んだりして、
自分なりの解釈でまとめたので誤りがありましたらごめんなさい。
一応、ドキュメントを見ながら全部載せしたつもり。
http://www.gnu.org/software/make/manual/

ちなみに、makeのバージョンは、

$ make --version
GNU Make 3.81

4.x系もリリースされているんですね...

文字列関連の関数

filter

TEXT内からPATTERNの文字列に一致する要素を取得する。
.cppファイルのみを取得したいときなどに使える。

使い方

$(filter PATTERN...,TEXT)

実行例

VAR := hoge.h hoge.cpp hogera.h hogera.cpp

.PHONY: all
all:
    @echo "$(filter %.cpp,$(VAR))"

結果

$ make
hoge.cpp hogera.cpp

filter-out

TEXT内からPATTERNの文字列に一致しない要素を取得する。
filterの逆バージョン。

使い方

$(filter-out PATTERN...,TEXT)

実行例

VAR := hoge.h hoge.cpp hogera.h hogera.cpp

.PHONY: all
all:
    @echo "$(filter-out %.cpp,$(VAR))"

結果

$ make
hoge.h hogera.h

findstring

IN内にFINDの文字列があるかどうかを確認する

使い方

$(findstring FIND,IN)

実行例

VAR := abc def ghi

.PHONY: all
all:
    @echo "$(findstring bc,$(VAR))"

結果

$ make
bc

firstword

NAMES内の最初の要素を取得する。

使い方

$(firstword NAMES...)

実行例

VAR := abc def ghi

.PHONY: all
all:
    @echo "$(firstword $(VAR))"

結果

$ make
abc

lastword

NAMES内の最後の要素を取得する。

使い方

$(lastword NAMES...)

実行例

VAR := abc def ghi

.PHONY: all
all:
    @echo "$(lastword $(VAR))"

結果

$ make
ghi

patsubst

TEXT内からPATTERNにマッチしたものをREPLACEMENTに置き換える。
※substのPATTERNが使えるバージョン。

使い方

$(patsubst PATTERN,REPLACEMENT,TEXT)

実行例

VAR := hoge.cpp hogera.cpp

.PHONY: all
all:
    @echo "$(patsubst %.cpp,%.o,$(VAR))"

結果

$ make
hoge.o hogera.o

sort

LISTの各要素をソートする。

使い方

$(sort LIST)

実行例

VAR := hoge foo hogera fuga

.PHONY: all
all:
    @echo "$(sort $(VAR))"

結果

$ make
foo fuga hoge hogera

strip

STRINGの無駄な空白を取り除く。

使い方

$(strip STRING)

実行例

VAR := a b    c    d

.PHONY: all
all:
    @echo "$(strip $(VAR))"

結果

$ make
a b c d

subst

TEXT内のFROMをTOに置き換える。
※patsubstのPATTERNが使えないバージョン。

使い方

$(subst FROM,TO,TEXT)

実行例

VAR := hoge.cpp hogera.cpp

.PHONY: all
all:
    @echo "$(subst .cpp,.o,$(VAR))"

結果

$ make
hoge.o hogera.o

word

TEXT内のN番目の要素を取得する。
※ 0 < N

使い方

$(word N,TEXT)

実行例

VAR := abc def ghi

.PHONY: all
all:
    @echo "$(word 1,$(VAR))"

結果

$ make
abc

wordlist

TEXT内のSからEまでの要素を取得する。
※wordのlistバージョン。

使い方

$(wordlist S,E,TEXT)

実行例

VAR := abc def ghi

.PHONY: all
all:
    @echo "$(wordlist 1,2,$(VAR))"

結果

$ make
abc def

words

TEXTの要素数を取得する

使い方

$(words TEXT)

実行例

VAR := abc def ghi

.PHONY: all
all:
    @echo "$(words $(VAR))"

結果

3

ファイル名関連の関数

abspath

NAMESの各要素の絶対パスを取得する。
※ファイルやディレクトリが存在するかどうかはチェックしない。

使い方

$(abspath NAMES...)

実行例

VAR := ../ ./ ./dir Makefile

.PHONY: all
all:
    @echo "$(abspath $(VAR))"

結果

$ make
/home/chibi /home/chibi/tmp /home/chibi/tmp/dir /home/chibi/tmp/Makefile

addprefix

NAMESの各要素の接頭辞としてPREFIXを追加する。
※-Iとかを付けるときなどに使える。

使い方

$(addprefix PREFIX,NAMES...)

実行例

VAR := ./include ./dir/include

.PHONY: all
all:
    @echo "$(addprefix -I,$(VAR))"

結果

$ make
-I./include -I./dir/include

addsuffix

NAMESの各要素の接尾辞としてSUFFIXを追加する。
※addprefixの接尾バージョン。
※今のところ使う機会がない。

使い方

$(addsuffix SUFFIX,NAMES...)

実行例

VAR := hoge.cpp hogera.cpp

.PHONY: all
all:
    @echo "$(addsuffix .bak,$(VAR))"

結果

$ make
hoge.cpp.bak hogera.cpp.bak

basename

NAMESの各要素のファイル名を取得する。
(拡張子を排除する)

使い方

$(basename NAMES...)

実行例

VAR := hoge.txt ./dir/hogera.txt

.PHONY: all
all:
    @echo "$(basename $(VAR))"

結果

$ make
hoge ./dir/hogera

dir

NAMESの各要素のディレクトリ名を取得する。

使い方

$(dir NAMES...)

実行例

VAR := hoge.txt ./dir/hogera.txt

.PHONY: all
all:
    @echo "$(dir $(VAR))"

結果

$ make
./ ./dir/

join

LIST1とLIST2の先頭から突合せ結合を行う。
LIST1とLIST2のサイズが異なる場合は、
「空文字列+LIST2」や「LIST1+空文字列」といった形で結合される。

使い方

$(join LIST1,LIST2)

実行例

VAR1 := a b c d
VAR2 := 1 2 3 4 5

.PHONY: all
all:
    @echo "$(join $(VAR1),$(VAR2))"

結果

$ make
a1 b2 c3 d4 5

notdir

NAMESの各要素のファイル名を取得する。

使い方

$(notdir NAMES...)

実行例

VAR := hoge.txt ./dir/hogera.txt

.PHONY: all
all:
    @echo "$(notdir $(VAR))"

結果

$ make
hoge.txt hogera.txt

realpath

NAMESの各要素の絶対パスを取得する。
abspathとは異なり、ファイルやディレクトリが存在するもののみ取得する

使い方

$(realpath NAMES...)

実行例

VAR := ../ ./ ./dir Makefile

.PHONY: all
all:
    @echo "$(realpath $(VAR))"

結果

$ make
/home/chibi /home/chibi/tmp /home/chibi/tmp/Makefile

suffix

NAMESの各要素の拡張子を取得する。
※basenameの拡張子バージョン。

使い方

$(suffix NAMES...)

実行例

VAR := hoge.txt ./dir/hogera.txt

.PHONY: all
all:
    @echo "$(suffix $(VAR))"

結果

.txt .txt

wildcard

ワイルドカードを使って実在するファイルを取得する。
PATTERNは複数個書くこともできる。

使い方

$(wildcard PATTERN)

実行例

VAR := ./tmp/*.cpp ./tmp/*.md

.PHONY: all
all:
    @echo "$(wildcard $(VAR))"

結果

$ make
./tmp/foo.cpp ./tmp/hoge.cpp ./tmp/README.md

条件関連の関数

and

全てのCONDITIONが空でないかどうかを取得する(?)
全てのCONDITIONが空ではない場合に最後のCONDITIONが返ってくる。1つでも空があると結果は空。
※こんな関数があったんだ...使う機会なさそう...

使い方

$(and CONDITION1[,CONDITION2[,CONDITION...]])

実行例

VAR1 := abc
VAR2 := def
VAR3 := ghi

.PHONY: all
all:
    @echo "$(and $(VAR1),$(VAR2),$(VAR3))"
    @echo "$(and $(VAR1),$(EMPTY),$(VAR3))"
    @echo "$(and $(VAR3),$(VAR2),$(VAR1))"

結果

$ make
ghi

abc

if

CONDITIONが空文字列だったらELSE_PARTを
空文字列じゃない場合はTHEN_PARTを取得する。(なのかな?)
※and と or の流れから行くと合ってる気はするのだが...
※条件分岐系は挙動の理解に時間かかった...

使い方

$(if CONDITION,THEN_PART[,ELSE_PART])

実行例

VAR1 := abc
VAR2 := def
VAR3 := ghi

.PHONY: all
all:
    @echo "$(if $(VAR1),$(VAR2),$(VAR3))"
    @echo "$(if $(EMPTY),$(VAR2),$(VAR3))"

結果

$ make
def
ghi

or

CONDITION全てが空文字列の場合は空を返す。
1つでも値がある場合はCONDITION1が返る。
※andを実行してからorをやったからなんとなく挙動の予想がついてた!
※が...どっちにしろ使わなそう...

使い方

$(or CONDITION1[,CONDITION2[,CONDITION...]])

実行例

VAR1 := abc
VAR2 := def
VAR3 := ghi

.PHONY: all
all:
    @echo "$(or $(VAR1),$(VAR2),$(VAR3))"
    @echo "$(or $(VAR1),$(EMPTY),$(VAR3))"
    @echo "$(or $(EMPTY),$(EMPTY),$(EMPTY))"
    @echo "$(or $(VAR3),$(VAR2),$(VAR1))"

結果

$ make
abc
abc

ghi

デバッグ関連の関数

error

MakefileのエラーとしてTEXTが取得され、かつStopされる。
※if関数とかと組み合わせて使うのかな?

使い方

$(error TEXT...)

実行例

.PHONY: all
all:
    @echo "$(error chibi)"

結果

Makefile:151: *** chibi.  Stop.

info

TEXTが取得される。

使い方

$(info TEXT...)

実行例

.PHONY: all
all:
    @echo "$(info chibi)"

結果

chibi

warning

行番号とTEXTが取得される。Stopにはならない。
※if関数とかと組み合わせて使うのかな?

使い方

$(warning TEXT...)

実行例

.PHONY: all
all:
    @echo "$(warning chibi)"

結果

Makefile:159: chibi

その他の関数

call

他の変数やマクロを呼び出すときに使用する。
呼び出し先では、PARAMを引数として$1,$2などで表す。

使い方

$(call VARIABLE,PARAM,...)

実行例

VAR = "var $1 $2"
define MACRO
    @echo "macro $1 $2"
endef

.PHONY: all
all:
    @echo "$(call VAR,a,b)"
    $(call MACRO,a,b)

結果

$ make
var a b
macro a b

eval

変数を展開した上で、Makefileの構文として定義される。
※実行例ではcall関数と組み合わせてみます。

使い方

$(eval TEXT)

実行例

PROGRAMS := server client
server_OBJS = server_a.o server_b.o server_c.o
client_OBJS = client_a.o client_b.o client_c.o

define template
$(1):
    @echo "$1:"
    @echo "$($1_OBJS)"
endef

.PHONY: all
all: $(PROGRAMS)

$(eval $(call template,server))
$(eval $(call template,client))

結果

$ make
server:
server_a.o server_b.o server_c.o
client:
client_a.o client_b.o client_c.o

$ make server
server:
server_a.o server_b.o server_c.o

$ make client
client:
client_a.o client_b.o client_c.o

flavor

変数の形式(?)を返す。
返される値は以下の通り。
・undefined: 未定義
・simple: Simply expanded variable
・recursive: recursively expanded variable

使い方

$(flavor VARIABLE)

実行例

DEFINED := test
RECURSIVE = test

.PHONY: all
all:
    @echo $(flavor UNDEFINED)
    @echo $(flavor DEFINED)
    @echo $(flavor RECURSIVE)

結果

undefined
simple
recursive

foreach

LISTの要素をVARに分解し、TEXTで展開して実行する。
関数名の通りfor系の関数。

※evalの実行例にforeachを追加してみよう。

使い方

$(foreach VAR,LIST,TEXT)

実行例

PROGRAMS := server client
server_OBJS = server_a.o server_b.o server_c.o
client_OBJS = client_a.o client_b.o client_c.o

define template
$(1):
    @echo "$1:"
    @echo "$($1_OBJS)"
endef

.PHONY: all
all: $(PROGRAMS)

$(foreach prog,$(PROGRAMS),$(eval $(call template,$(prog))))

結果

server:
server_a.o server_b.o server_c.o
client:
client_a.o client_b.o client_c.o

$ make server
server:
server_a.o server_b.o server_c.o

$ make client
client:
client_a.o client_b.o client_c.o

origin

どこで定義された変数なのかが取得される。
取得される文字列は以下の通り。
・undefined: 未定義
・default: CC や MAKE など
・environment: PATHなどの環境変数
・environment override:
・file: Makefile内で定義されたもの
・command line: コマンドラインで定義されたもの
・override: Makefile内でオーバーライドされたもの
・automatic: 自動変数

使い方

$(origin VARIABLE)

実行例

DEFINED := test
OVERRIDE := test
override OVERRIDE := test

.PHONY: all
all:
    @echo $(origin UNDEFINED)
    @echo $(origin DEFINED)
    @echo $(origin PATH)
    @echo $(origin OVERRIDE)
    @echo $(origin @)
    @echo $(origin MAKE)

結果

undefined
file
environment
override
automatic
default

shell

shellを叩く。
lsコマンドとかでwildcardの代わりなどもできる。

使い方

$(shell COMMAND)

実行例

.PHONY: all
all:
    @echo "$(shell ls)"

結果

Makefile tmp

value

変数を参照する関数(?)
$(VARIABLE)と同じ結果が返ってくる。

使い方

$(value VARIABLE)

実行例

VAR := test

.PHONY: all
all:
    @echo "$(VAR)"
    @echo "$(value VAR)"

結果

test
test

4.x系で追加された関数

file

ファイル書き込みを行える。
上書きと追記の2種類のモードがある。

※ついでにこの関数の実験中にわかったことが、
※shell関数を使うと改行コードはスペースに変換されてMakefileで扱う形になるということ。
※ダブルクォーテーションも無くなってた。

使い方

$(file OP FILENAME[,TEXT])

実行例

.PHONY: all
all: 
    @echo "all"
    $(file > test.txt,"A")
    @echo "$(shell cat test.txt)"
    $(file >> test.txt,"B")
    @echo "$(shell cat test.txt)"
    cat test.txt

結果

A
A B
cat test.txt
"A"
"B"

guile

GNU Guileが統合されたためMakefileからguileが使えるようになっているとのこと。

以下を参考にチャレンジ
http://qiita.com/ka_/items/4aa58cd180e535767977

GNU Guile自体、
全然わからないのでstring-appendの真似をする。

使い方

$(guile SCHEME)

実行例

all:
    @echo $(guile (string-append "Hello" "World!"))

結果

HelloWorld!
mashandroom
会社外活動・コミュニティとして、ハッカソンや各種イベントのボランティアなどに参加。ただただ自分たちが楽しみ、結果周りの人が喜んでくれるようなアウトプットを目指しています。創造せよ!頭にキノコが生えるまで
https://mashandroom.org
Why not register and get more from Qiita?
  1. We will deliver articles that match you
    By following users and tags, you can catch up information on technical fields that you are interested in as a whole
  2. you can read useful information later efficiently
    By "stocking" the articles you like, you can search right away