Edited at

Makefileの関数

More than 1 year has 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!