Makefileの関数

  • 157
    いいね
  • 0
    コメント

自分用にずっとまとめようと思って、下書き保存して温めていた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!