Make基础
Make:根据指定的Makefile文件构建新文件
| 12
 
 | $ make [-f makefile] [<target>]
 
 | 
- make默认寻找当前目中GNUmakefile/makefile/Makefile文件作为配置
文件
- 默认用makefile中首个目标文件作为最终目标文件,否则
使用<target>作为目标文件
Make参数
- -b/- -m:忽略和其他版本make兼容性
 
- -B/- --always-make:总是更新/重编译所有目标
 
- -C <dir>/- --directory=<dir>:指定读取makefile的目录,
相当于- $ cd <dir> && make
 - 
- 指定多个-C <dir>,make将按次序合并为最终目录
- -C时,- -w选项自动打开
 
- --debug[=<options>]:输出make调试信息
 - 
- a:all,输出所有调试信息
- b:basic,基本信息
- v:verbose,基本信息之上
- i:implicit,所有隐含规则
- j:jobs,执行规则中命令的详细信息,如:PID、返回码
- m:makefile,make读取、更新、执行makefile的信息
 
- -d:等价于- --debug=a
 
- -e/- --environment-overrides:环境变量覆盖makefile中值
 
- -f <file>/- --file=<file>/- --makefile=<file>:指定
makefile
 - 
- 可以多次传递参数-f <filename>,所有makefile合并
传递给make
 
- -h/- --help:帮助
 
- -i/- --ignore-errors:忽略执行时所有错误
 
- -I <dir>/- --include-dir=<dir>:搜索- includemakefile
路径
 
- -j [<jobsum>]/- -jobs[=<jobsum>]:指定同时运行命令数
 - 
- 进程数
- 默认同时运行尽量多命令
- 多个-j时最后者生效
 
- -k/--keep-going:出错不停止运行
 
- -l <load>/- --load-average[=<load>]
 
- --max-load[=<load>]:make命令负载
 
- -n/- --just-print/- --dry-run/- --recon:仅输出执行
过程中命令序列,不执行
 
- -o <file>/- --old-file=<file>/- --assume-old=<file>:
不重新生成指定的- <file>,即使目标依赖其
 
- -p/- --print-data-base:输出makefile中所有数据:规则、
变量等
 | 12
 3
 4
 
 | $ make -qp# 只想输出信息,不执行makefile
 $ make -p -f /dev/null
 # 查看执行makefile前的预设变量、规则
 
 |  
 
- -q/- --question:不执行命令、不输出,仅检查指定目标
是否需要更新
 
- -r/- --no-builtin-rules:禁用make任何隐含规则
 
- -R/- --no-builtin-variables:禁用make任何作用于变量上
的隐含规则
 
- -s/- --silent/- quiet:禁止显示所有命令输出
 
- -S/- --no-keep-going/- --stop:取消- -k作用
 
- -t/- --touch:修改目标日期为最新,即组织生成目标的命令
执行
 
- -v/- --version:版本
 
- -w/- --print-directory:输出运行makefile之前、之后信息
 
- --no-print-directory:禁止- -w选项
 
- -W <file>/- --what-if=<file>/- --new-file=<file>/- --assume-file=<file>
 - 
- 联合-n选项,输出目标更新时的运行动作
- 没有-n,修改<file>为当前时间
 
- --warn-undefined-variables:有未定义变量是输出警告信息
 
步骤
- 读入所有makefile
- 读入被include其他makefile
- 初始化(展开)文件中变量、函数,计算条件表达式
- 展开模式规则%、推导隐式规则、分析所并规则
- 为所有目标文件创建依赖关系链
- 根据依赖关系,决定需要重新生成的目标
- 执行生成命令
相关环境变量
Makefile基本语法
控制符号
引用其他Makefile
- <filename>可以是默认shell的文件模式,包含通配符、路径
- include之前可以有空格,但是不能有- <tab>(命令提示)
- make寻找其他makefile,将其内容放当前位置 
- 若文件没有明确指明为绝对路径、相对路径,make会在以下目录
中寻找 - 
- -I、- --include-dir参数
- /usr/local/bin/include、- /usr/include
 
- make会- include环境变量MAKEFILES中文件
 - 
- 不建议使用环境变量MAKEFILES,会影响所有make
 
- 文件未找到,make会生成一条警告信息,但继续载入其他文件,
完成makefile读取之后,make会重试寻找文件,失败报错 - 
- 可以使用-include/sinclude代替,忽略include过程
中的任何错误
 
Makefile显式规则
| 12
 
 | <target>: <prerequisite>[tab]<commands>
 
 | 
- <target>:目标
- <prerequisites>:前置条件
- <commands>:命令,和前置条件至少存在一个
| 12
 
 | a.txt: b.txt c.txtcat b.txt c.txt > a.txt
 
 | 
Target
目标:make的目标
- 目标通常是文件名,指明需要构建的对象
- 不是文件名的目标称为伪目标,视为某种操作
多目标
多目标规则意义是多个目标共享规则依赖、声明命令,并
不是需要同时生成多个目标
- 需要多目标中的任何一个时,多目标规则就会被应用,其中
命令被执行 
- 每次只生成单独目标的多目标规则,目标之间只是单纯的
可以合并简化规则中的命令 | 12
 3
 4
 5
 6
 7
 8
 9
 
 | bigoutput littleoutput: text.ggenerate text.g -$(subst output,,$@) > $@
 
 
 
 bigoutput: text.g
 generate text.g -big > bigoutput
 littleoutput: text.g
 generate text.g -little > littleoutput
 
 |  
 
- 同时生成多个目标的多目标规则,多个目标应该满足
需要同时生成、不能单独修改,否则没有必要定义为多目标
,当然这其实也是合并简化规则中的命令 | 12
 
 | %.tab.c %.tab.h: %.ybison -d $<
 
 |  
 
Phony Target
todo
伪目标:目标是某个操作的名字,每次执行都会执行命令
| 12
 3
 4
 
 | .PHONY: clean
 clean:
 rm *.o
 
 | 
- 若省略.PYHONY,要求当前目中不存在同名文件,否则make认为目标已存在,不会执行命令
GNU规范
GNU推荐makefile中包含的伪目标、命名
- all:编译所有目标
- clean:删除所有被make创建的文件
- install:安装已编译好程序,即将目标执行文件拷贝到指定
目标中
- print:列出改变过的源文件
- tar:打包源程序备份
- dist:创建压缩文件
- tags:更新所有目标,以备完整地编译使用
- check/- test:测试makefile流程
静态库
目标archive(member):指定静态库文件、及其组成
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | foolib(hack.o kludge.o): hack.o kludge.oar cr foolib hack.o kludge.o
 
 foolib(hack.o): hack.o
 ar cr foolib hack.l kudge.o
 foolib(kludge.o): kludge.o
 ar cr foolib kludge.o
 
 
 
 
 foolib(*.o): hack.o kludge.o
 
 
 | 
Prerequisites
前置条件/依赖:生成目标的依赖
- 通常是一组空格分隔的文件名,为空则说明目标的生成和其他
文件无关 
- 指定目标是否重新构建的判断标准,只要有一个前置文件不存在
、有过更新(时间戳比较),目标就需要更新 
- 若前置文件不存在,则需要建立以其作为目标的规则用于生成,
- make target时会自动调用
 
| 12
 
 | source: file1 file2 file3
 
 | 
Commands
命令:更新、构建文件的方法
- 在linux下默认使用环境变量SHELL(/bin/sh)执行命令,
- 在MS-DOS下没有SHELL环境变量,会在PATH环境变量中寻找
,并自动加上.exe、.bat、.sh等后缀
<tab>
每行命令前必须有一个<tab>,否则需要使用提前声明
| 12
 3
 
 | .RECIPEPREFIX=>all:
 > echo Hello, world
 
 | 
Shell进程
每行命令在单独的shell进程中执行,其间没有继承关系
(即使是同一个规则中)
- 多条命令可以使用- ;分隔
 | 12
 
 | var-kept:export foo=bar; echo "foo=[$$foo]"
 
 |  
 
- 可类似python- \换行
 | 12
 3
 
 | var-kept:export foo=bar; \
 echo "foo=[$$foo]"
 
 |  
 
- 使用- .ONESHELL命令
 | 12
 3
 4
 
 | .ONESHELLvar-kept:
 export foo=bar
 echo "foo=[$$foo]"
 
 |  
 
嵌套执行Make
大工程中不同模块、功能源文件一般存放在不同目录,可以为每个
目录单独建立makefile
- 利于维护makefile,使得其更简洁
- 利于分块/分段编译
- 最顶层、调用make执行其他makefile的makefile称为总控
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | subsystem:cd subdir && $(MAKE)
 
 subsystem:
 $(MAKE) -C subdir
 
 subsystem:
 cd subdir && $(MAKE) -w MAKEFLAGS=
 
 
 
 | 
搜索路径
VPATH
VPATH:makefile中定义的特殊环境变量,指明寻找依赖文件、
目标文件的路径
vpath
vpath:make关键字,指定不同模式文件不同搜索目录
| 12
 3
 4
 5
 6
 7
 
 | vpath <pattern> <directories>vpath %.h ../headers
 # 指明`<pattern>`模式文件搜索目录
 vpath <pattern>
 # 清除`<pattern>`模式文件搜索目录设置
 vpath
 # 清除所有`vapth`设置
 
 | 
- <pattern>中使用- %匹配0或若干字符
- vpath可以重复为某个模式指定不同搜索策略,按照出现顺序
  先后执行搜索
隐含规则
常用隐含规则
编译C
- 目标:- <n>.o
 
- 依赖包含:- <n>.c
 
- 生成命令 | 1
 | $(CC) -c $(CPPFLAGS) $(CFLAGS)
 |  
 
编译CPP
- 目标:- <n>.o
 
- 依赖包含- <n>.cc/- <n>.c
 
- 生成命令 | 1
 | $(CXX) -c $(CPPFLAGS) $(CFLAGS)
 |  
 
编译Pascal
编译Fortran/Ratfor
- 目标:- <n>.o
 
- 依赖包含:- <n>.f/- <n>.r
 
- 生成命令 | 12
 3
 4
 5
 6
 
 | $(FC) -c $(FFLAGS)
 $(FC) -c $(FFLAGS) $(CPPFLAGS)
 
 $(FC) -c $(FFLAGS) $(RFLAGS)
 
 
 |  
 
预处理Fortran/Ratfor
- 目标:- <n>.f
 
- 依赖包含:- <r>.r/- <n>.F
 
- 生成命令 | 12
 3
 4
 
 | $(FC) -F $(CPPFLAGS) $(FFLAGS)
 $(FC) -F $(FFLAGS) $(RFLAGS)
 
 
 |  
 
- 转换Ratfor、有预处理的Fortran至标准Fortran
编译Modula-2
- 目标:- <n>.sym/- <n>.o
 
- 依赖包含:- <n>.def/- <n>.mod
 
- 生成命令| 12
 3
 4
 
 | $(M2C) $(M2FLAGS) $(DEFFLAGS)
 $(M2C) $(M2FLAGS) $(MODFLAGS)
 
 
 |  
 
汇编汇编
- 目标:- <n>.o
 
- 依赖包含:- <n>.s
 
- 生成命令:默认使用编译器- as
 
预处理
- 目标:- <n>.s
 
- 依赖包含:- <n>.S
 
- 生成命令:默认使用预处理器- cpp
 
链接object
- 目标:- <n>
 
- 依赖包含:- <n>.o
 
- 生成命令:默认使用C工具链中链接程序- ld
 | 1
 | $(CC) <n>.o $(LOADLIBS) $(LDLIBS)
 |  
 
Yacc C
Lex C
Lex Ratfor
创建Lint库
- 目标:- <n>.ln
 
- 依赖包含:- <n>.c/- <n>.y/- <n>.l
 
- 生成命令 | 1
 | $(LINT) $(LINTFLAGS) $(CPPFLAGS) -i
 |  
 
创建静态链接库
- 目标:- <archive>(member.o)
 
- 依赖包含:- member
 
- 生成命令 | 1
 | ar cr <archive> member.o
 |  
 
- 即使目标传递多个memeber.o,隐含规则也只会解析出把首个.o文件添加进静态链接库中的命令
| 12
 3
 4
 
 | (%.o): %.o$(AR) rv $@ $*.o
 
 
 
 | 
隐含规则使用变量
隐含规则使用的变量基本都是预先设置的变量
- makefile中改变
- make命令环境变量传入
- 设置环境变量
- -R/- --no-builtin-variable参数取消变量对隐含规则作用
命令
- AR:函数打包程序,默认- ar
- AS:汇编语言编译程序,默认- as
- CC:C语言编译程序,默认- cc
- CXX:C++语言编译程序,默认- c++/- g++
- CPP:C程序预处理器,默认- $(CC) -E/- cpp
- FC:Fortran、Ratfor编译器、预处理程序,默认- f77
- PC:Pascal语言编译程序,默认- pc
- LEX:Lex文法分析器程序(针对C、Ratfor),默认- lex
- YACC:Yacc文法分析器程序(针对C、Ratfor),默认- yacc -r
- GET:从- SCCS文件中扩展文件程序,默认- get
- CO:从- RCS文件中扩展文件程序,默认- co
- MAKEINFO:转换Texinfo- .texi到Info程序,默认- makeinfo
- TEX:转换TeX至Tex DVI程序,默认- tex
- TEXI2DVI:转换Texinfo至Tex DVI程序,默认- texi2dvi
- WEAVE:转换Web至TeX程序,默认- weave
- TANGLE:转换Web至Pascal程序,默认- tangle
- CTANGEL:转换C Web至C,默认- ctangle
- RM:删除文件命令,默认- rm -f
命令参数
未指明默认值则为空
- ARFLAGS:静态链接库打包程序AR参数,默认- rv
- ASFLAGS:汇编语言汇编器参数
- CFLAGS:C编译器参数
- CXXFLAGS:C++编译器参数
- CPPFLAGS:C预处理参数
- LDFLAGS:链接器参数
- FFLAGS:Fortran编译器参数
- RFLAGS:Fatfor的Fortran编译器参数
- LFLAGS:Lex文法分析器参数
- YFLAGS:Yacc文法分析器参数
- COFLAGS:RCS命令参数
- GFLAGS:SCCS- get参数
隐含规则链
make会努力自动推导生成目标的一切方法,无论中间目标
数量,都会将显式规则、隐含规则结合分析以生成目标
模式规则
模式规则:隐式规则可以看作内置模式规则
- 目标定义包含%,表示任意长度非空字符串
- 依赖中同样可以使用%,但是其取值取决于目标
- 命令中不使用模式%,使用自动化变量
静态模式
静态模式:给定目标候选范围的模式,限制规则只能应用在以
给定范围文件作为目标的情况
| 12
 
 | <target>: <target-pattern>: <prereq-patterns><commands>
 
 | 
- <target>:目标候选范围,可含有通配符
- <target-pattern>:所有目标文件满足的模式
- <prereq-pattern>:目标相应依赖
- 简单例子 | 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | objects = foo.o bar.oall: $(objects)
 $(objects): %.o: %.c
 $(CC) -c $(CFLAGS) $< -o $@
 
 
 
 foo.o: foo.c
 $(CC) -c $(CFLAGS) foo.c -o foo.o
 bar.o: bar.c
 $(CC) -c $(CFLAGS) bar.c -o bar.o
 
 |  
 
- 静态模式+- filter函数筛选范围
 | 12
 3
 4
 5
 
 | files = foo.elc bar.o lose.o$(filter %.o,$(files)): %.o: %.c
 $(CC) -c $(CFLAGS) $< -o $@
 $(filter %.elc,$(files)): %.elc: %.el
 emacs -f batch-byte-compile $<
 
 |  
 
重载内建隐含规则
| 12
 3
 4
 5
 
 | %.o: %c$(CC) -c $(CPPFLAGS) $(CFLAGS) -D $(date)
 
 %o: %c
 
 
 | 
后缀规则
- 双后缀规则:定义一对目标文件后缀、依赖后缀
- 单后缀规则:定义一个依赖后缀
| 12
 3
 4
 5
 6
 
 | .c.o:
 $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
 .c:
 
 $(CC) -c $(CFLAGS) $(CPPFLAGS) -o $@ $<
 
 | 
- 后缀规则中不能有任何依赖文件,否则不是后缀规则,后缀被
认为是目标文件 
- 后缀规则中必须有命令,否则没有任何意义,这不会移去内建
的隐含规则 
- 后缀规则定义中的后缀需要是make所认识的,可以使用伪目标
- .SUFFIXES修改make认识的后缀
 | 12
 3
 4
 
 | .SUFFIXES:
 .SUFFIXES: .c .o .h
 
 
 |  
 
- 后缀规则是老式定义隐含规则的方法,会被逐步取代,事实上
后缀规则在makefile载入后会被转换为模式规则 
模式规则搜索算法
设目标为src/foo.o
- 将目标目录部分、文件名部分分离,得到- src/、- foo.o
 
- 搜索所有模式规则列表,创建目标和- src/foo.o匹配的模式
规则列表
 - 
- 若模式规则列表中有目标匹配所有文件的模式(如%),
则从列表中移除其他模式
- 移除列表中没有命令的规则
 
- 对列表中首个模式规则 - 
- 将src/foo.o或foo.o匹配目标,推断%匹配非空部分
茎S
- 把依赖中%替换为茎S,如果依赖项中没有包含目录,
尝试将src添加在其前
- 检查所有依赖文件存在、理当存在(文件被定义为其他规则
的目标文件、显式规则的依赖文件)
- 若有依赖文件存在、理当存在或没有依赖文件,此规则被
采用,退出算法
 
- 若没有找到合适模式规则,则检查列表中下个规则是否可行 
- 若没有模式规则可以使用,检查- .DEFAULT规则存在性,存在
则应用
 
变量、赋值
Makefile中定义的变量类似C++/C中的宏
- 在shell中需要$$处应使用两个$$$,一个$被escape,则
  shell解释时仍然保留一个$,如:变量、函数等都需要
赋值
Makefile内自定义变量
| 12
 3
 4
 5
 6
 7
 
 | txt = Hello World
 test:
 @echo $(txt)
 echo ${txt}
 
 
 
 | 
define
define可以换行定义变量
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | define run-yacc
 yacc $(firstword $^); \
 mv y.tab.c $@
 endef
 
 
 foo.c: foo.y
 $(run-yacc)
 
 
 | 
override
- 若变量由make命令行参数-e设置,makefile中默认忽略对其
赋值
- 需要显式使用override关键字设置
| 12
 3
 
 | override <variable> = <value>override <variable> := <value>
 override define <variable>
 
 | 
export
上级makefile中变量可以显式export传递到下层makefile中,
但是不会覆盖下层中定义的变量(除指定-e参数)
| 12
 3
 4
 5
 6
 7
 8
 9
 
 | export <variable>[=value]
 unexport <variable>
 
 
 export variable = value
 
 variable = value
 export variable
 
 | 
- export后面不指定具体传递变量,表示传递所有变量
- MAKEFLAGS、- SHELL两个变量总是会传递到下级makefile中
系统环境变量
make运行时系统环境变量、命令行环境变量可以被载入makefile
- 默认makefile中定义变量覆盖系统环境变量
- -e参数则表示makefile中变量被覆盖
Target-Specific Variable
目标/局部变量:作用范围局限于规则、连带规则中
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 
 | <target ...>: [override] <variable-assignment>
 prog: CFLAGS  = -g
 prog: prog.o foo.o bar.o
 $(CC) $(CFLAGS) prog.o foo.o bar.o
 
 prog.o: prog.c
 $(CC) $(CFLAGS) prog.c
 
 foo.o: foo.c
 $(CC) $(CFLAGS) foo.c
 
 bar.o: bar.c
 $(CC) $(CFLAGS) bar.c
 
 | 
Pattern-Specific Variable
模式变量:给定模式,变量定义在符合模式的所有目标上
| 12
 3
 
 | <pattern ...>: [override]<variable-assignment>
 %.o: CFLAGS = -o
 
 | 
Implicit Variables
内置变量:主要是为了跨平台的兼容性
- $(CC):当前使用的编译器
 | 12
 
 | output:$(CC) -o output input.c
 
 |  
 
- $(MAKE):当前使用的Make工具
 
- $(MAKECMDGOLA):make目标列表
 
Automatic Variables
自动化变量:应用规则时被自动赋予相应值(一般是文件)的变量
- 自动化变量只应出现在规则的命令中
- 自动化变量值与当前规则有关
- 其中$@、$*、$<、$%扩展后只会为单个文件,$?、$^、$+扩展后可能是多个文件
| 12
 3
 
 | dest/%.txt: src/%.txt@[ -d test ] || mkdir dest
 cp $< $@
 
 | 
D、F
- 7个自动化变量可以搭配D、F取得相应路径中目录名、
文件名
- 新版本GNU make可以使用函数dir、notdir代替D/F
- D/- dir:目录带有最后- /,若为当前目录则为- ./
- F/- nodir:文件名
- 对可能会扩展为多文件的$?、$^、$+,D/F处理后
  返回同样是多个目录/文件
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | $(@D)$(dir $@)
 
 $(@F)
 $(nodir $@)
 
 
 $(?D)
 $(dir $?)
 
 $(?F)
 $(nodir $?)
 
 
 | 
控制语句
if
| 12
 3
 4
 5
 6
 7
 8
 
 | <conditional-directive>
 <text-if-true>
 [
 else
 <text-if-false>
 ]
 endif
 
 | 
- ifeq:比较参数是否相等
 
- ifneq:比较参数是否不等
 | 12
 3
 4
 5
 6
 
 | ifeq ($(CC), gcc)
 libs=$(libs_for_gcc)
 else
 libs=$(normal_libs)
 endif
 
 |  
 
- ifdef
 | 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 
 | bar =foo = $(bar)
 
 ifdef foo
 frobozz = yes
 
 else
 frobozz = no
 endif
 
 foo =
 
 ifdef foo
 frobozz = yes
 else
 frobozz = no
 
 endif
 
 |  
 
- ifndef
 
- <conditional-directive>, else, endif行可以有多余空格,
  但是不能以- <tab>开头,否则被认为是命令
- make在读取makefile时就计算表达式值、选择语句,所以最好
  别把自动化变量放入条件表达式中
- make不允许把条件语句分拆放入两个文件中
for
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 
 | LIST = one two three
 all:
 for i in $(LIST); do \
 echo $$i;
 // 这里传递给shell的变量,需要`$$` escape
 done
 all:
 for i in one two three; do
 echo $$i;
 done
 
 | 
内建函数
| 12
 
 | $(function parameters)${function paremeters}
 
 | 
Make控制函数
提供一些函数控制make执行
- 检测运行makefile的运行时信息,根据信息决定make继续执行
、停止
error
产生错误并退出make,错误信息<text>
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 
 | $(error <text...>)
 ifdef ERROR_001
 $(error error is $(ERROR_001))
 endif
 
 ERR = $(error found an error)
 .PHONY: err
 err:
 err: ; $(ERR)
 
 | 
warning
类似error函数,输出警告信息,继续make
其他函数
shell
执行shell命令的输出作为函数返回值
| 1
 | srcfiles := $(shell echo src/{00..99}.txt)
 | 
- 函数会创建新shell执行命令,大量使用函数可能造成性能下降
,尤其makefile的隐晦规则可能让shell函数执行次数过多
wildcard
在变量中展开通配符*
| 12
 3
 
 | srcfiles := $(wildcard src/*.txt)
 
 
 | 
字符串处理函数
subst
文本替换
| 12
 3
 4
 5
 
 | $(subst <from>,<to>,<text>)
 
 $(subst ee,EE,feet on the street)
 
 
 | 
patsubst
模式匹配的替换
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 
 | $(patsubst <pattern>,<replacement>,<text>)
 $(patsubst %.c,%o,x.c.c bar.c)
 
 
 foo := a.o b.o c.o
 $(variable: <pattern>=<replacement>)
 
 bar := $(foo:%.o=%.c)
 
 
 $(variable: <suffix>=<replacement>)
 
 bar := $(foo:.o=.c)
 
 
 | 
strip
去字符串头、尾空格
| 12
 
 | $(strip <string>)$(strip a b c)
 
 | 
findstring
在<in>中查找<find>,找到返回<find>,否则返回空串
| 12
 
 | $(findstring <find>,<in>)$(findstring a,a b c)
 
 | 
filter
以<pattern>模式过滤<text>字符串中单词,返回符合模式的
单词
| 12
 3
 4
 5
 6
 
 | $(filter <pattern..>,<text>)
 sources := foo.c bar.c baz.s ugh.h
 foo: $(sources)
 cc $(filter %.c %.s, $(sources)) -o foo
 
 
 | 
filter-out
以<pattern>模式过滤<text>字符串中单词,返回不符合模式的
单词
| 12
 3
 4
 
 | objects := main1.o foo.o main2.o bar.omains=main1.o main2.o
 $(filter-out $(mains), $(objects))
 
 
 | 
sort
对<list>中单词升序排序
| 12
 3
 4
 
 | $(sort <list>)
 $(sort foo bar lose)
 
 
 | 
word
取字符串<text>中第<n>个单词
| 12
 3
 4
 
 | $(word <n>,<text>)
 $(word 2, foo bar baz)
 
 
 | 
wordlist
从<text>中取<s>-<e>单词(闭区间)
| 12
 3
 4
 
 | $(wordlist <s>,<e>,<text>)
 $(wordlist 2, 3, foo bar baz)
 
 
 | 
words
统计<text>中单词个数
| 12
 3
 4
 
 | $(word <text>)
 $(word, foo bar baz)
 
 
 | 
firstword
取<text>中首个单词
| 12
 3
 4
 
 | $(firstword <text>)
 $(firstword foo bar)
 
 
 | 
文件名操作函数
dir
从文件名序列中取出目录部分
| 12
 3
 4
 
 | $(dir <names...>)
 $(dir src/foo.c hacks)
 
 
 | 
notdir
从文件名序列中取出非目录部分(最后/之后部分)
| 12
 3
 4
 
 | $(notdir <names...>)
 $(notdir src/foo.c hacks)
 
 
 | 
suffix
从文件名序列中取出各文件名后缀
| 12
 3
 4
 
 | $(suffix <names...>)
 $(suffix src/foo.c src-1.0/bar.c hacks)
 
 
 | 
basename
从文件名序列中取出各文件名“前缀”(除后缀外部分)
| 12
 3
 4
 
 | $(basename <names...>)
 $(basename src/foo.c src-1.0/bar.c hacks)
 
 
 | 
addsuffix
把后缀<suffix>添加到文件名序列中每个单词后
| 12
 3
 4
 
 | $(addsuffix <suffix>,<names...>)
 $(addsuffix .c, foo bar)
 
 
 | 
addprefix
把后缀<prefix>添加到文件名序列中每个单词后
| 12
 3
 4
 
 | $(addprefix <prefix>,<names...>)
 $(addprefix src/, foo bar)
 
 
 | 
join
把<list2>中单词对应添加到<list1>中单词后
| 12
 3
 4
 
 | $(join <list1>,<list2>)
 $(join aaa bbb, 111 222 333)
 
 
 | 
控制函数
foreach
循环函数,类似于Bash中的for语句
- 把<list>中单词逐一取出放到参数<var>所指定的变量中
- 再执行<text>所包含的表达式,每次返回一个字符串
- 循环结束时,返回空格分隔的整个字符串
| 12
 3
 4
 5
 
 | $(foreach <var>,<list>,<text>)
 names := a b c d
 files := $(foreach n,$(names),$(n).o)
 
 
 | 
if
类似于make中的ifeq
- <condition>为真(非空字符串),计算- <then-part>返回值
- <condition>为假(空字符串),计算- <else-part>、返回空
字符串
| 1
 | $(if <condition>,<then-part>,[<else-part>])
 | 
call
创建新的参数化函数的函数 
- 创建表达式<expression>,其中可以定义很多参数
- 用call函数向其中传递参数,<expression>返回值即call返回值
| 12
 3
 4
 5
 6
 7
 8
 
 | $(call <expression>,<param1>,<param2>,...>
 reverse = $(1) $(2)
 foo = $(call reverse,a,b)
 
 reverse = $(2) $(1)
 foo = $(call reverse,a,b)
 
 
 | 
- <expression>要先创建为变量,然后不带- $传递
origin
返回变量的来源
- undefined:- <variable>未定义
- default:make默认定义变量
- environment:环境变量,且- -e选项未开
- file:定义在makefile中
- command line:命令行定义环境变量
- override:- override关键字定义
- atomatic:命令运行中自动化变量
| 12
 3
 4
 5
 6
 7
 
 | $(origin <variable>)
 ifdef bletch
 ifeq "$(origin bletch)" "environment"
 bletch = barf, gag, etc
 endif
 endif
 
 | 
Makefile技巧
案例
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 18
 19
 20
 21
 22
 23
 24
 25
 26
 27
 28
 
 | edit: main.o kdd.o command.o display.o \insert.o search.o files.o utils.o
 cc -o edit main.o kbd.o command.o dispaly.o\
 insert.o search.o files.o utils.o
 
 main: main.c defs.h
 cc -c main.c
 kbd.o: kbd.c defs.h
 cc -c kbd.c
 command.o: command.c defs.h command.h
 cc -c command.c
 display.o: display.o defs.h buffer.h
 cc -c display.c
 insert.o: insert.c defs.h buffer.h
 cc -c insert.c
 search.o: search.c defs.h buffer.h
 cc -c search.c
 files.o: files.c defs.h buffer.h command.h
 cc -c files.c
 utils.o utils.c defs.h
 cc -c utils.c
 
 clean:
 rm edit main.o kbd.o command.o display.o \
 insert.o search.o files.o utils.o
 
 .PHONY: edit clean
 
 
 | 
利用变量简化目标
| 12
 3
 4
 5
 6
 
 | objects = main.o kbd.o command.o display.o \insert.o search.o files.o utils.o
 
 edit: $(objects)
 cc -o edit $(objects)
 
 
 | 
隐式模式自动推导
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 14
 15
 16
 17
 
 | objects = main.o kbd.o command.o display.o \insert.o search.o files.o utils.o
 edit: $(objects)
 cc -o edit $(objects)
 main.o: defs.h
 kbd.o: defs.h command.h
 command.o: defs.h command.h
 display: defs.h buffer.h
 insert.o: defs.h buffer.h
 search.o: defs.h buffer.h
 files.o: defs.h buffer.h command.h
 utils.o: defs.h
 
 clean:
 rm edit $(objects)
 
 .PHONY: clean
 
 | 
利用变量提取依赖
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | objects = main.o kbd.o command.o display.o \insert.o search.o files.o utils.o
 edit: $(objects)
 cc -o edit $(objects)
 
 $(objects): defs.h
 kbd.o command.o files.o: command.h
 display.o insert.o search.o files.o: buffer.h
 
 clean:
 rm edit $(objects)
 
 .PHONY: clean
 
 | 
自动生成依赖
| 12
 3
 4
 5
 6
 7
 8
 9
 10
 11
 12
 13
 
 | %.d: %.c@set -e; rm -f $@; \
 $(cc) -M $(CPPFLAGS) $< > $@.$$$$; \
 
 
 sed 's,/($*/)/.o[ :]*,/1.o $@ : ,g' < $@.$$$$ > $@; \
 
 
 rm -f $@.$$$$
 
 
 source = foo.c bar.c
 include $(sources: .c=.d)
 
 |