関数(Function)(組み込み関数)
POINT
Make の組み込み関数は文字列操作が主な目的
文字列置換 と同じ
call は 関数.Call というより 特殊な文字列置換
list 中から 目的の file を選択
変数参照に見えるが , 区切りの Argument をもつ
何かの値に展開され, 変数への代入 , Shell にわたされる
POINT
Command 列を 変数に格納することで 応用範囲が広がる
Macro, 変数 への展開結果をかえれるように Argument をわたせる
Make の関数には 数値 という型はない( 文字列として扱う )
has-duplicate = $(filter \
$(word $1) \
$(words $(sort $1)))
関数が Nest される場合は, 最下層から ひとつずつ読む
file-to-class-name := $(subst /,., \ # "/" -> "."
$(subst .java,, \ # ".java" を除去
$(subst $1/,,$2))) # "$1/" を除去
SYNTAX
$(function arg[, arg...])
echo $1
$(call MACRONAME arg, [arg...])
POINT
[.] Macro, 変数 の違いは [\n] が組み込まれるかどうかだけ
WARNING
[,] 以降の SPC は arg として解釈
$(call intro, yamada) -> $1 = [ yamada]
$(function %.obj)
POINT
[.] Windows は File を開くと, 他の Process からの書き込みを防止するため
File を Lock する
POINT
Make の EmbededFunc の共通点
[.] Text をもらって, ある操作をして 別の Text をかえす
[.] Argument は [,] 区切り
[.] Argument のひとつとして SPACE 区切りの 単語 List をもらう
単語間の Space は区切り扱いだが, それ以外は無視
[.] Pattern 指定も OK
[%] : 0 コ以上の文字
文字列関数
filter
SYNTAX:
$(filter ptn ...,text )
DESC
text を " " 区切り文字として ptn 一致のものを取得
grep みたいなもの
$(ui_lib): $(filter ui/%.o,$(OBJS))
$(AR) $(ARFLAGS) $@ $^
words := he the hen other the%
get-the:
@echo he matches $(filter he, $(words))
@echo %he matches $(filter %he, $(words))
WARNING
単語は一部を含むではなく, すべて一致する必要あり
ptn[%] は一度しか使用できない
複数あれば 最初のみ ptn[%]
残りは文字あつかい( [%]という文字にマッチ )
filter-out
SYNTAX
$(filter-out ptn ...,text )
DESC
ptn に一致しない text 内の単語をかえす
$(filter-out ptn, text)
srcs := main.c sub.c main.h sub.h
to_compile := $(filter-out %.h, $(srcs))
findstring
SYNTAX
$(findstring serStr,text)
DESC
text 中の serStr を検索
見つかれば serStr( 検索文字列 ) 自身をかえす
wildcard[%] は使用不可
if() とあわせると便利
TIP
[#]: make の変換結果を見るための, echo より簡易な方法
find-tree:
subst
SYNTAX
$(subst serStr,repStr,text)
DESC
文字列( text ) を置換( Substitute )
[%] の指定はできない
POINT
SRCS := main.c sub.c foo.c
$(subst .c,.o,$(SRCS))
WARNING
$(subst .c ,.o,(SRCS))
$(subst .c, .o,(SRCS))
$(subst .c,.o,(SRCS))
patsubst
SYNTAX
$(patsubst serPtn,repPtn, text)
DESC
wildcard 使用の検索、置換
[%]: は match した文字列に置換される
WARNING
strip-tail-slash = $(patsubst %/,%,$(dirPath))
BAD
%/ -> [aaa/bbb/]ccc
OK
%/ -> [aaa/bbb/ccc/]
$(variable:ser=rep)
ser: 通常文字列 -> 単語の末尾に一致 -> rep に置換
OBJS := $(SRCS:.cpp=.o)
ser: [%]文字列 -> patsubst 同様
words
SYNTAX
$(words text)
DESC
単語数をかえす
CURPATH := $(subst /, ,$(HOME))
words:
echo $$HOME has $(words $(CURPATH)) Directory
CURPATH := $(subst /, ,$(HOME))
words:
@echo $(HOME)
@echo my home path has $(words $(CURPATH))dirs
@echo first dir is $(word 1, $(CURPATH))
@echo second dir is $(word 2, $(CURPATH))
word
SYNTAX
$(word n,text)
DESC
[n] 番目の text を返す
1 が最初
n > nrText ならば 何も返さない
POINT
current := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
firstword
SYNTAX
$(firstword text)
DESC
1 番目の 単語をかえす
== $(word 1,text)
version_list := $(subst ., ,$(MAKE_VERSION))
major_version := $(firstword $(version_list))
wordlist
SYNTAX
$(wordlist start,end,text)
DESC
[start:end] list をかえす
start > $(words text)
start > end
なら何も返さない
end > $(words text)
なら [start:$(words text)]をかえす
uid_gid = $(wordlist 3,4 \
$(subst :, ,\
$(shell grep "^$1:" /etc/passwd)
)
)
call
SYNTAX
$(call cmd,arg...)
sort
SYNTAX
$(sort list)
DESC
単語の重複, 前後の SPACE を除去してソートする
make -f- <<< 'x:;@echo =$(sort a n d d c e)='
shell
SYNTAX
$(shell cmd)
DESC
ひとつの Argument をうけつけて, 展開後に SubShell にわたす
STDOUT への出力は 結果としてかえる
[\] は " "に置換される
STDERR, Exit Status は返されない
POINT
$(shell cmd) は subshell 実行後の結果に置換される
stdout := $(shell echo normal msg)
stderr := $(shell echo err msg 1>&2)
shell-value:
$(shell date)
$(shell oman test)
POINT
_MKDIRS 自体は意味はない
$(shell) が実行されることが目的
REQ_DIRS := foo bar goo
_MKDIRS := $(shell for d in $(REQ_DIRS); \
do \
[[ -d $$d ]] || mkdir -p $$d; \
done)
WARNING
単純変数 と 再帰変数の違いに注意すること
ST := $(shell date)
CT := $(shell date) # CT が利用されるたびに評価
RELEASE_TAR := mpwm-$(shell date +%F).tar.gz
file-to-class-name := $(subst /,.,$(patsubst %.java,%,$1))
file.名関数
wildcard
SYNTAX
$(wildcard ptn...)
DESC
ptn に match した file 名に展開
一致しないなら ""
TIP
ptn は Target, Depend で展開されるのと同じ
Shell の 展開文字 を利用する
[ ~, *, ?, [...], [^...] ]
"%" をまちがって利用しないように
SRCS := $(shell ls *.cpp)
$(wildcard ./dir/*.cpp)
SRCS := $(wildcard *.c *.h)
POINT
Directory .cpp をすべて取得可能
SRCS := main.cpp sub.cpp ... といったベタガキは不要
dotemacs := $(wildcard ~/.emacs)
WARNING
Current Directory 基準( 再帰的ではない )
$(wildcard *.cpp)
dir|notdir
SYNTAX
$(dir list ...)
DESC
list の各単語の Directory 部分を返す
srcdir:=$(sort \
$(dir \
$(shell find . -iname "*.cpp")))
$(call srcdir,/pro/intp)
srcdir=$(sort \
$(dir \
$(shell find $1 -iname "*.cpp")))
@echo $(call getshbody,test.sh)
getshbody=$(notdir $(subst .sh,,$1))
$(OUT)/myfile.out: $(SRC)/src.in
cd $(dir $@); \
generate-myself $^ > $(notdir $@)
notdir filename
test.txt
$(notdir "d:/test.txt")
suffix
SYNTAX
$(suffix name ...)
DESC
list から suffix をかえす
$(suffix test.bat)
same-suffix = $(filter 1,$(words $(sort $(suffix $1))))
TIP
findstring 関数と共に if() で使用
basename
SYNTAX
$(basename list...)
DESC
File Suffix を除去した文字列をかえす
test:
echo $(basename main.cpp sub.cpp)
file-to-class-name := $(subst /,., \
$(basename \
$(subst $1/,,$2)
)
)
get-java-class-name = $(notdir $(basename $1))
addsuffix
SYNTAX
$(addsuffix suffix list)
DESC
list の各単語に suffix 追加
POINT
wildcard の patten は shell と同じ
filter は [%] のみ
find-program = $(filter $1 # 最後に filtering
$(wildcard # wildcard で検索
$(addsuffix /*, # wildcard 用に "/*" を追加
$(sort # sort
$(subst :, , # PATH 区切り[:] -> " " 単語に分解
$(subst ::,:.:,
$(patsubst :%,.:%,
$(patsubst :%,:.,$(PATH)
)
)
)
)
)
)
)
addprefix
SYNTAX
$(addprefix pfxk,name...)
DESC
validfile = test -s . $(addprefix -a -s ,$1)
join
SYNTAX
$(join pfxlist,sfxlist)
DESC
prefix list, suffix list の各単語を連結
dir notdir で分離した単語を結合できる
fname := yamada suzuki
sname := taro itiro
fullname := $(join $(fname),$(sname))
実行制御
POINT
[.] 実行制御をすることで幅広い処理ができる
[.] error も本質的には 実行制御
if
SYNTAX
$(if cond,then-part,else-part)
DESC
cond が "文字列を含めば" : 真
NULL 文字列 : 偽
cond の前後の空白は除去した後, 結果がチェックされる
true ならば then-part マクロが展開
false ならば else-part マクロが展開
PATH_SEP := $(if $(COMSPEC),;,:)
POINT
$(if $(filter $(MAKE_VERSION),3.80),, \
$(error this ver in not 3.80) )
POINT
Macro 言語 と Programing 言語は異なる
Macro 言語 は Macro の定義, 展開をして 結果をえるのが Macro 言語
error
error
SYNTAX
$(error text)
DESC
Error 報告をして
ExitCode 2 をかえす
define assert
$(if $1,,$(error assertion failed: $2))
endef
define assert-exit-file
$(call assert $(wildcard $1),$1 not exist)
endef
define assert-not-null
$(call assert $($1),var $1 is null)
endef
POINT
[.] Make の変数 は 他の変数も代入できる
[.] 変数がはいった 変数を展開するには 2 回展開する
(call assert-not-null NULLVAR)
warning
SYNTAX
$(warning text)
DESC
警告 Message を表示
error とは違い, Make は終了しない
POINT
[.] makefile( FILE ), LINE を表示してくれる
POINT
[.] 展開されないので, すべての場所で利用できる
foreach
SYNTAX
$(foreach var,list,body)
DESC
list の単語を順に var に設定して, body を展開
展開された body の結果は " "区切りで累積する
SPC 区切りの文字列 list として展開
tmp:
@echo $(foreach idx,1 2 3 4 5 6 7 8 9,$music_$idx.wmv)
define grep-string
$(strip
$(foreach w,$2
$(if $(findstring $1,$w),$w))))
endef
list := foo.h bar.h foo.cpp
test:
echo $(call grep-string,foo,$(list))
POINT
[.] Make の関数は PreProcessor と同じ
[.] その場で展開するため, 展開後の結果がおきかわる
VAR := foo
$(call grep-string,foo,$(list))
letters := $(foreach letter,a b c d,$(letter))
show-words:
VARLIST := SRCS OBJS HOME
$(foreach i,$(VARLIST),\
$(if $($1),,$(shell echo $i has no value > /dev/stderr))\
)
strip
SYNTAX
$(strip text)
DESC
前後の空白除去と
list 間の空白をひとつにする
[ aaa bbb ccc ] -> [aaa bbb ccc]
TIP
$(if) 判定の引数をきれいにするのに利用する
$(call func,arg1, arg2)
VAR:= aaa bbb ccc
test:
echo $(VAR)
echo __$(strip $(VAR))__
origin
SYNTAX
$(origin var)
DESC
変数の出所を調べる
undefined: 未定義
default: 組み込み ( DB で定義すみ )
enviroment: ENV
enviroment override: (-e)--enviroment-overrides
file: makefine 定義
command line: CommandLine 定義
override : overwride 命令
automatic : 自動変数
$(origin MAKE_VERSION) # 変数名自体
make testtgt DATE=`date`
$(origin DATE) # cmdline
define assert-defined
$(call assert,\
$(filter-out undefined,$(origin $1)),\
$1 is undefined)
endef
高度なユーザー定義関数
POINT
Debug 機能は多くないので自前で用意すること
POINT
[=] を利用するのは, := では関数の評価に問題あり
debug-enter = $(if $(debug-trace),\
($warning Enter $0$(echo-args)))
debug-leave = $(if $(debug-trace),\
($warning Leave $0))
comma := ,
echo-args = $(subst ' ','$(comma) ',\
$(foreach a,1,2,3,4,5,'$($a)'))
debug-trace = 1
define a
$(debug-enter)
echo $1 $2 $3
$(debug-leave)
endef
define b
$(debug-enter)
$(call a,$1,$2,hi)
$(debug-leave)
endef
test:
$(call b,5,$(MAKE))
eval.関数と.val
SYNTAX:
$(eval exp)
DESC
Exp を Make の構文解析に直接渡す( eval 自体は値を返さない )
POINT
EXp が make に描かれていたことを 考えれば OK
POINT
[.] Make の関数はまず 引数が変数の場合,展開する
$(eval SRCS:=foo.c bar.c)
define pro-var
$1_s = $(filter %.c,$2)
$1_h = $(filter %.h,$2)
$1_o = $(subst .c,.o,$(filter %.c,$2))
$($1_o): $($1_h)
endef
$(call pro-var,ls,ls.c ls.h glob.c glob.h)
$(eval $(call pro-var,ls,ls.c ls.h glob.c glob.h))
TIP
複数行の macro を扱う
2. 関数置換後の str を再評価
2. eval の引数は 2度展開される
make が eval を展開する
2. eval 自身
POINT
[.] Macro とは define によって定義される 特殊な変数
( ie. Macro は変数のひとつ )
[.] Macro と 変数の違いは , Macro が [\n] を含めることができること
POINT
eval を使う理由は, 複数の変数を展開をするため
POINT
複数行に 展開される Macro を makfile の Top で利用するには eval() にわたすこと
WARNING
最初の Macro の展開だけでは, Macro 内の変数の代入はされない
ls_s = ls.c
ls_h = ls.h
ls_o = ls.o
$(ls_o): $(ls_h)
$$($1_o) : $$($1_h)
POINT
[.] まとめ
[.] eval の引数は 2 回展開される
[.] 1 回目 : Make が eval の引数を展開
[.] 2 回目 : eval 自身の展開
define pro-var
$(eval $1_s = $(filter %.c,$2))
$(eval $1_h = $(filter %.h,$2))
$(eval $1_o = $(filter %.o,$2))
$($1_o): $($1_h)
endef
POINT
まとめとして, 次のように使う
define pro-var
$(eval $1_s = $(filter %.c,$2))
$(eval $1_h = $(filter %.h,$2))
$(eval $1_o = $(filter %.o,$2))
programs += $1
$1: $($1_o)
$($1_o): $($1_h)
endef
$(eval $(call pro-var ls,ls.c ls.h ))
$(eval $(call pro-var mv,mv.c mv.h ))
WARNING
複数行に展開される macro は構文 ERROR
hook.関数
POINT
[.] User 定義関数とは 単に 文字列を格納した変数のこと
[.] call は 関数という文字列 の中で $1, $2 があれば, 引数に置換するだけ
そのため変数に文字列がなくても何もしない
$(call func,foo,bar)
$(call invalidfunc,foo,bar)
POINT
[.] hook を利用すると 関数がさらに便利になる
[.] user は hook に自分の好きな処理を追加できる
define build-library
$(AR) $(ARFLAGS) $@ $1
$(call build-library-hook,$@)
endef
$(foo_lib): build-library-hook = $(RANLIB) $1
$(foo_lib): $(fooobj)
$(call build-library,$^)
$(bar_lib): build-library-hook = $(CHMOD) 444 $1
$(bar_lib): $(barobj)
$(call build-library,$^)
関数に値を渡す
DESC
call を利用
2. 関数外の変数
3. tgt 固有変数