POINT 
    Make の組み込み関数は文字列操作が主な目的
    文字列置換 と同じ
    call は 関数.Call  というより 特殊な文字列置換
    
    list 中から 目的の file を選択
    変数参照に見えるが , 区切りの Argument をもつ
    何かの値に展開され, 変数への代入 , Shell にわたされる
  POINT 
    Command 列を 変数に格納することで 応用範囲が広がる
    Macro, 変数 への展開結果をかえれるように Argument をわたせる
    Make の関数には 数値 という型はない( 文字列として扱う )
    # (call has-duplicate, list) 
    # 数値も 文字列として比較 
    # 
    has-duplicate = $(filter                    \
                      $(word $1)             \
                       $(words $(sort $1)))
 
    # (call file-to-class-name  rootdir file) 
    file-to-class-name := $(subst /,.,             \    # "/"  -> "."  
                          $(subst .java,,       \      # ".java"  を除去 
                            $(subst $1/,,$2)))         # "$1/"  を除去 
 SYNTAX 
      $(function arg[, arg...])
    # Argument の参照は $N とする 
    echo $1
    # 展開するには call 組み込み関数 を利用 
    #    call は 関数 というより 
    #    特殊な マクロ展開 
    # 
    $(call MACRONAME arg, [arg...])
  POINT 
  WARNING 
    [,] 以降の SPC は arg として解釈
    #  ","   の後の " "  は不要 
    $(call intro, yamada) -> $1 = [ yamada]
 
    # パターンの適用も OK  
    #    [%] == 0文字以上 
    $(function %.obj)
 POINT 
    POINT 
    Make の EmbededFunc の共通点
■ 文字列関数 
■ filter 
  SYNTAX :
      $(filter ptn ...,text )
  DESC 
     text を " "  区切り文字として ptn 一致のものを取得
    grep みたいなもの
    # ui/*.o  のみを filter する 
    # 
      $(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 内の単語をかえす
      # ptn 不一致を取得 
      $(filter-out ptn, text)
 
  srcs := main.c sub.c main.h sub.h
  # xxx.h を除去する 
  to_compile := $(filter-out %.h, $(srcs))
 ■ findstring 
  SYNTAX 
      $(findstring serStr,text)
  DESC 
    text 中の serStr を検索
    見つかれば serStr( 検索文字列 ) 自身をかえす
    wildcard[%] は使用不可
    if() とあわせると便利
  TIP 
    [#]: make の変換結果を見るための, echo より簡易な方法 
  # 複数の Tree ( src, DebugBinary, ReleaseBinary のどこにいるか調べる) 
   find-tree:
      # PWD = $(PWD) 
      # Tab から始まるため, Make は SubShell への Command と解釈 
      #    Make 関数で置換してから わたす 
      # 
      # $(findstring d:/0916, $(PWD)) 
      # $(findstring d:/0915, $(PWD)) 
      # $(findstring d:/0914, $(PWD)) 
 ■ subst 
  SYNTAX 
    $(subst serStr,repStr,text)
  DESC 
    文字列( text ) を置換( Substitute )
    [%] の指定はできない
  POINT 
    # ext を変更するときに便利 
    #  .c -> .o 
    # 
    SRCS := main.c sub.c foo.c
    $(subst .c,.o,$(SRCS))
 WARNING 
    # [,] の後の空白は文字扱いになる 
    # ERROR 
    #    ".c "   が  ".o"   にかわる 
    $(subst .c ,.o,(SRCS))
    # ERROR 
    #    "main.c"   --->  "main .o"  
    $(subst .c, .o,(SRCS))
    # OK  
    #  RET : main.o  sub.o  foo.o 
    $(subst .c,.o,(SRCS))
 ■ patsubst 
  SYNTAX 
    $(patsubst serPtn,repPtn, text)
  DESC 
    wildcard 使用の検索、置換
    [%]: は match した文字列に置換される
  WARNING 
    # 末尾 の [/] を除去 
    #    serPtn: text 全体で一致する必要あり 
    #    途中の [/] は置換されない 
    # 
    strip-tail-slash = $(patsubst %/,%,$(dirPath))
 
  BAD 
    %/ -> [aaa/bbb/]ccc
  OK 
    %/ -> [aaa/bbb/ccc/]
 ■ words 
  SYNTAX 
    $(words text)
  DESC 
     単語数をかえす
    CURPATH := $(subst /, ,$(HOME))
    words:
          echo $$HOME has $(words $(CURPATH)) Directory
    # [/] -> [SPC] 変換 
    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 
    # list 最後の取得 
    current := $(word $(words $(MAKEFILE_LIST)),$(MAKEFILE_LIST))
 ■ firstword 
  SYNTAX 
    $(firstword text)
  DESC 
    1 番目の 単語をかえす
    == $(word 1,text)
    # 単語 List に変換 
    #    "xxx.yyy.zzz"  -> "xxx yyy zzz"  
    # 
    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)]をかえす
    # $(call uid_gid username) 
    # 
    uid_gid = $(wordlist 3,4 \
                $(subst :, ,\
                   $(shell grep "^$1:"  /etc/passwd)
                  )
                )
 ■ call 
  SYNTAX 
    $(call cmd,arg...)
■ sort 
  SYNTAX 
     $(sort list)
  DESC 
    単語の重複, 前後の SPACE を除去してソートする
    # STDIN から makefile の内容を入力 
    # 
    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)
    # STDERR への出力は shell 関数の戻値にならない 
    # 
      shell-value:
          # $(stdout) 
          # $(stderr) 
 
      # RET : [Mon Sep 17 10:41:29] 
      # make がこの行を解釈すれば ERR 
      $(shell date)
      # こちらは OK  
         $(shell oman test)
 # make をはじめる前に 必要な dir を作成する 
    # bash の for 文を利用して Directory 作成 
    # 
    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 が利用されるたびに評価 
      # date uniq file を作成 
      RELEASE_TAR := mpwm-$(shell date +%F).tar.gz
    # $(call file-to-class-name, filename) 
    #  相対 Path を Java クラス名に変換 
    # 
    file-to-class-name := $(subst  /,.,$(patsubst %.java,%,$1))
■ file.名関数 
■ wildcard 
  SYNTAX 
    $(wildcard ptn...)
  DESC 
    ptn に match した file 名に展開
   一致しないなら "" 
  TIP 
    ptn は Target, Depend で展開されるのと同じ
    Shell の 展開文字 を利用する
    [ ~, *, ?, [...], [^...] ]
   "%"  をまちがって利用しないように
    #  $(shell) で代用するとこうなる 
    SRCS := $(shell ls *.cpp)
 
    # shell の ptn match 同様( Directory 込みで match ) 
    # RET  
    #      ./dir/aaa.cpp ./dir/bbb.cpp 
     $(wildcard ./dir/*.cpp)
 
    #  xxx.c, xxx.h   をセット 
    SRCS := $(wildcard *.c *.h)
 POINT 
    Directory .cpp をすべて取得可能
   SRCS := main.cpp sub.cpp ...  といったベタガキは不要
   #  if と併用することで File の有無をチェック 
   # 
   dotemacs := $(wildcard ~/.emacs)
 WARNING 
    Current Directory 基準( 再帰的ではない )
    # Sub Directory にはマッチしない 
    #  RET : 
    #    ./foo.cpp ./bar.cpp 
    $(wildcard *.cpp)
 ■ dir|notdir 
  SYNTAX 
     $(dir list ...)
  DESC 
     list の各単語の Directory 部分を返す
     # src Directory 検索 
    # 
     srcdir:=$(sort               \
              $(dir             \
                $(shell find . -iname "*.cpp" )))
 # $(call srcdir,dirlist) 
      # WARNING  
      #    即時評価では問題あり 
      $(call srcdir,/pro/intp)
      srcdir=$(sort         \
              $(dir       \
                  $(shell find $1 -iname "*.cpp" )))
      @echo $(call getshbody,test.sh)
      getshbody=$(notdir $(subst .sh,,$1))
    # File の出力先と同じ Directory で実行 
    # 
      $(OUT )/myfile.out: $(SRC)/src.in
              cd $(dir $@);                       \
              generate-myself $^ > $(notdir $@)
 ■ suffix 
  SYNTAX 
      $(suffix name ...)
  DESC 
    list から suffix をかえす
    # RET  
    #    bat 
     $(suffix test.bat)
      # 同一 suffix Check 
      same-suffix = $(filter 1,$(words $(sort $(suffix $1))))
 TIP 
    findstring 関数と共に if() で使用
■ basename 
  SYNTAX 
    $(basename list...)
  DESC 
    File Suffix を除去した文字列をかえす
    # RET  
    #     main sub 
    test:
      echo $(basename main.cpp sub.cpp)
    # $(call file-to-class-name  rootdir  filename) 
    file-to-class-name := $(subst /,.,           \
                            $(basename        \
                              $(subst $1/,,$2)
                            )
                          )
    # $(call get-java-class-name  filename) 
    # 
    #  Java のクラス名をかえす 
    # 
    get-java-class-name = $(notdir  $(basename $1))
 ■ addsuffix 
  SYNTAX 
      $(addsuffix suffix list)
  DESC 
     list の各単語に suffix 追加
  POINT 
    wildcard の patten は shell と同じ
    filter は [%] のみ
 
    # $(call find-program, %) 
    find-program = $(filter $1                  # 最後に filtering 
                    $(wildcard                # wildcard で検索 
                        $(addsuffix /*,        #  wildcard 用に "/*"  を追加 
                          $(sort               # sort 
                             $(subst :, ,      # PATH 区切り[:] -> " "  単語に分解 
                                $(subst ::,:.:,
                                  $(patsubst :%,.:%,
                                    $(patsubst :%,:.,$(PATH)
                                   )
                                )
                             )
                          )
                        )
                    )
                  )
 ■ addprefix 
  SYNTAX 
      $(addprefix pfxk,name...)
  DESC 
      # file が空でないかテスト 
      # "-a -s "  が prefix 
      # Command 内で利用されることを想定 
      # 
      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 
    ■ if 
  SYNTAX 
    $(if cond,then-part,else-part)
  DESC 
     cond が "文字列を含めば"  : 真
     NULL 文字列            : 偽
    cond の前後の空白は除去した後, 結果がチェックされる
    true  ならば then-part マクロが展開
    false ならば else-part マクロが展開
    # PATH 区切り文字を決める 
    # 
    #  $(COMSPEC) は windows のみ定義 
    # 
     PATH_SEP := $(if $(COMSPEC),;,:)
 POINT 
      # if - filter で 文字列のチェックをする 
    # Version Check 
      $(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 をかえす
    # make assert 
    #   $(call assert cond,msg) 
    # 
    define assert
      $(if $1,,$(error assertion failed: $2))
    endef
    # 
    # make assert 
    #      $(call assert-exit-file filePtn,msg) 
    #      wildcard を利用して filePtn にマッチする file があるかチェック 
    # 
    define assert-exit-file
      $(call assert $(wildcard $1),$1 not exist)
    endef
    # make assert 
    # $(call assert-not-null makeVar) 
    # 
    # WARNING  
    #        VOID:=aiueo 
    #        $(call assert-not-null VOID) != $(VOID) 
    # 
    define assert-not-null
      # $($1) とあるのは 引数 として 変数をもらうため 
      # 
      $(call assert $($1),var $1 is null)
    endef
 POINT 
    
    # NULLVAR 
    # RET  
    #      "NULLVAR is null"  
    # 
    (call assert-not-null  NULLVAR)
 ■ warning 
  SYNTAX 
     $(warning text)
  DESC 
    警告 Message を表示
    error とは違い, Make は終了しない
  POINT 
    POINT 
    ■ foreach 
  SYNTAX 
      $(foreach var,list,body)
  DESC 
    list の単語を順に var に設定して, body を展開
    展開された body の結果は " " 区切りで累積する
     SPC 区切りの文字列 list として展開
    # RET  
    #    music_1.wmv music_2.wmv music_3.wmv ... 
    # 
    tmp:
        @echo $(foreach idx,1 2 3 4 5 6 7 8 9,$music_$idx.wmv)
 
    # $(call grep-string  serstr, list) 
    # 
    #    List から serstr を含む文字のみ抽出 
    # 
    define grep-string
      $(strip
        $(foreach w,$2
           $(if  $(findstring $1,$w),$w))))
    endef
    list := foo.h bar.h foo.cpp
    # RET  
    #    "foo.h foo.cpp"  
    test:
        echo $(call grep-string,foo,$(list))
 POINT 
    
    VAR := foo
    #  TopLevel で展開された結果は ERROR 
    $(call grep-string,foo,$(list))
 #        letter has $(words $(letters)) words: $(letters) 
      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) 判定の引数をきれいにするのに利用する
    #  [,] の後の " "  が arg2 に含まれてしまう 
    # 
    $(call func,arg1, arg2)
 
   VAR:= aaa         bbb       ccc
   test:
     echo $(VAR)
     echo __$(strip $(VAR))__
   # echo __aaa         bbb       ccc   __ 
   # __aaa bbb ccc __ 
   # echo __aaa bbb ccc__ 
   # __aaa bbb ccc__ 
 ■ 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 
    [=] を利用するのは, := では関数の評価に問題あり 
    # 関数 call を Trace 
    # 
    debug-enter = $(if $(debug-trace),\
                    ($warning Enter $0$(echo-args)))
    debug-leave = $(if $(debug-trace),\
                    ($warning Leave $0))
    # WARNING  
    #    引数の数を数えることはできない( 数も固定 ) 
    # 
    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 
      $(eval SRCS:=foo.c bar.c)
 # $(call pro-var,pfx,list) 
      # 
      #    list から 変数 $1_s, $1_h  $1_o に file をセットする 
      # 
      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
      # lss=ls.c glob.c < - make が tgt としてみなした 
      # 
      # WARNING  
      #    TopLevel では 複数行の Macro は Error になる 
      #    [区切り文字] がない といわれる 
      #    eval はこれを解消するためにできた 
      # 
      # 
      $(call pro-var,ls,ls.c ls.h glob.c glob.h)
      # 
      # $(eval lss=ls.c glob.c) # 代入式が評価される 
      # 
      $(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 
    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 
    # これでも OK  
  #    eval を代入に利用することで, Macro 内でおこなう 
  # 
      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
      # TopLevel で展開する 
      #    POINT  
      #      文字列の置換にすぎないことを覚えておくこと 
      # 
      $(eval  $(call  pro-var ls,ls.c  ls.h ))
      $(eval  $(call  pro-var mv,mv.c  mv.h ))
  WARNING 
     複数行に展開される macro は構文 ERROR
■ hook.関数 
  POINT 
    #  $1, $2 を引数に展開 
    # 
     $(call func,foo,bar)
    # これは ERROR にならない 
    # 
     $(call invalidfunc,foo,bar)
 POINT 
    
    # ar 後 後処理を build-library-hook という macro で用意しておく 
    define build-library
      $(AR) $(ARFLAGS) $@ $1
      $(call build-library-hook,$@)
    endef
    # hook を使うには 
    #    hook 関数( という変数 )に処理をいれる 
    # 
    $(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 固有変数