1. compilation and linking
compilation (object file): .obj files in windows; .o files in linux
编译时, 编译器会检查语法, 函数, 及变数的宣告正确与否, 正确则编译成功, 反之则失败!
linking: 告知编译器 head file 的位置, 链接函数和全局变数, so 可使用 object files 来链接应用程式.
当 object file 太多时通常习惯将其打包, 如 .lib in windows; .a in linux.
2. makefile rule
target : prerequisites
<Tab>command
或是
target : prerequisites ; command
prerequisites 中如果有一个以上的档案比 taget 还新, command 就会被执行, command 是一个 linux shell script.
3. The example of GNU make user guild
supposed that we have an application with 8 c files and 3 head files, we can write a makefile to meet below condictions.
1) All c files have to be compliered and linked if the application was never complied.
2) Just complie these c files and link them to the application if some c files were modified.
3) Just complie those c files that included these head files and link them to the application if some head files were modified.
makefile -
edit : main.o kbd.o command.o display.o / insert.o search.o files.o utils.o cc -o edit main.o kbd.o command.o display.o / insert.o search.o files.o utils.o main.o : main.c defs.h cc -c main.c kbd.o : kbd.c defs.h command.h cc -c kbd.c command.o : command.c defs.h command.h cc -c command.c display.o : display.c 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
4. 隐晦规则与变量使用
make 可自动识别寻找 <filename>.c 来编译 <filename>.o,这称为 makefile 的隐晦规则.
将上例的所有的 object files 都以一个变量 objects 表示, 相当于 string variable. 于是 makefile 可简化成:
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.o : 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 .PHONY : clean clean : rm edit $(objects)
上例中 ".PHONY" 的用法表示 clean 是一个伪目标文件, 上例中的 makefile 也可以写成:
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 .PHONY : clean clean : rm edit $(objects)
clean 一个比较 safe 的写法是: ("-" 表示当文件出现问题时, ignore 继续作后面的事)
.PHONY : clean clean : -rm edit $(objects)
5. 引用其他的 makefile
若要引用其他的 makefile, 可用 "include <filename>", 如 include a.mk b.mk c.mk
mak 执行时, 有 "-I" or "--include-dir" 参数, 则 make 会在指定的目录寻找, 如果有 <prefix>/include, 通常是 /usr/include or /usr/local/bin 存在的话, 也会寻找.
若希望忽略这个错误可用 "-include <filename>", 在其他 make 版本兼容的语法为 sinclude.
另环境变数 MAKEFILES, 亦等同此作用, 不同的是不同的档案以 "," 分开表示.
6. 搜寻的目录
vpath, 如 vpath %.h foo:bar, 表示先在 foo 目录下寻找, 若找不到, 再到 bar 下寻找
7. 多目标与自动化变量
往下看一个例子:
objects = foo.o bar.o all: $(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
$< 表示所有依赖的文件, 而 $@ 表示需生成的目标
$%: 目标中的规则成员名, 如目标是 foo.a, 则 $% = foo.a, 如果目标不是库文件, 则 $% 为空
$?: 所有比目标新的依赖文件集合
$^: 所有依赖目标的集合
$+: 不去除重复的依赖目标
$*: 目标样式中 "%" 及 "%" 之前的部分, 如目标是 "dir/a.foo.c", 样式为 "a.%.c", 则 $* = "dir/a.foo"
可以在自动化变量中指定是目录还是档案, 使用关键字 "D" 与 "F", 如 $(@D) 列举所有目标的目录
8. 调试
使用 "@" 在命令之前, 则命令将不会被 make 显示出来, 只会执行.
另 make -n or make --just-print, 只会显示命令, 而不执行命令, 而 make -s or make --silent, 则不显示命令.
有时候的命令出错, 并不一定是真正的错误, 如 mkdir, 此时则可在命令前加个 "-", 另外有个全局的做法, make -i or make --ignore-errors.
还有一个用法是 make -k or make --keep-going, 指的是若某个规则出错, 则往下执行其他规则.
9. 嵌套与变量传递
如有一个子目录为 subdir, 要进入 subdir 执行 make, 则 makefile 可写为:
subsystem:
cd subdir && $(MAKE)
相等于
subsystem:
$(MAKE) -C subdir
在此的 makefile 所定义的变量是不会覆盖到下层 makefile 所使用的变量, 除非用 "-e" (如果是 make -e, 系统环境变量将覆盖 makefile 所定义的变量)
如果要传递变量到下层 makefile, 可以用 "export", 反之为 "unexport", 意思是不传递参数到下层 makefile.
export var = val
等同于
var = val
export var
也同于
var := val
export var
(export var := val)
":=" 与 "=" 差别的地方是前面不能使用后面的变量, 即时后面被重复赋值, 如下: $y = bar, 而不是 "foo bar"
y := $(x) bar
x := foo
再看另外一个 export 的例子:
export var += val
亦等同于
var += val
export var
如果要传递所有的变量, 只要声明 "export" 后面啥么都不用加.
特别要注意的是 MAKEFLAGS, 这是一个环境变量, 将会将上层的 makefile 所使用的参数传递到下层, 所以如果不想传递的话, 可以这样使用:
subsystem:
cd subdir && $(MAKE) MAKEFLAGS=
另外还有 make -w or make --print-subdirectory, 会在 make 的过程中显示目录的进入及离开:
make: Entering directory `/home/hchen/gnu/make', 会在完成下层的 make 后离开目录
make: Leaving directory `/home/hchen/gnu/make'
另外值得一提的是, 当使用参数 "-C", 则 "-w" 会被自动打开, 当使用 "-s" or "--no-print-subdirectory", 那么 "-w" 的作用将失效.
10. 定义命令
"define' 开头, "endef' 结尾.
define run-func
func $(firstworld $^)
mv f.temp.c $@
endef
11. 变量
如果令一个变量的值是一个空格, 可以下列写法,
nullstring :=
space := $(nullstring) # end of line
或是
empty :=
space := $(empty) $(empty)
还有一个比较有用的操作符是 "?=", 如果 foo 没有被定义过, 则 foo = bar, 否则忽略.
foo ?= bar
等同于
ifeq ($(origin foo), undefined)
foo = bar
endif
回顾一下前面提到的变量值替换: 结尾 .o 换成 .c
foo := a.o b.o c.o
bar := $(foo:%.o=%.c)
等价于
foo := a.o b.o c.o
bar := $(foo:.o=.c)
与 Perl 一样具有变量中的变量机制, 看看两个等同例子, 其输出都是 "Hello"
x = $(y) y = z z = Hello a := $($(x))
等同于:
x = variable1 variable2 := Hello y = $(subst 1,2,$(x)) z = y a := $($($(z)))
subst 是把 $x 中的 "1' 替换成 "2".
make 预设以接在 make 之后的参数作为预设的参数, 也就是等同于 ":=" 的使用, 如果 makefile 中想在覆盖这些参数, 可以使用 override 指示符:
override a = b
override a := b
override a += b
override define foo
bar
endef
谈谈目标变量, 在规则规则内忽略全局变量, 不管 CFLAGS 全局变量为何, 在 prog 目标内, CFLAGS 永远为 -g
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
12. 条件表示式
ifeq, ifneq, ifdef, ifndef, 以下为示例:
bar = foo = $(bar) ifdef foo frobozz = yes else frobozz = no endif
13. 函数
makefile 中函数的使用以 "$" 来标示, 看看以下示例, 空格以",", 则其结果 bar = 'a,b,c".
comma:= , empty:= space:= $(empty) $(empty) foo:= a b c bar:= $(subst $(space),$(comma),$(foo))
1) 字符串函数
subst <arg1> <arg2> <arg>: 将 arg 中的 <arg1> 替换成 <arg2>
patsubst <pattern> <replace> <arg>: 将 arg 中符合 <pattern> 替换成 <replace>
strip <arg>: 将 <arg> 中开头跟结尾的空格去除
findstring <find> <target>: 在 <target> 寻找 <find>, 找到返回 <find>, 反之返回 "" (空字符串)
filter <pattern> <arg>: 保留 <arg> 中, 符合 <pattern> 的单词
filterout <pattern> <arg>: 与 filter 相反, 保留不符合 <pattern> 的单词
sort <list>: 将 <list> 的单词升序排列
word <number> <text>: 取第 <number> 个单词, 从 1 开始.
wordlist <from>, <to>, <list>: 从第 <from> 到 <to>, 将 <list> 单词取出
words <list>: 返回单词个数
firstword <list>: 返回第一个单词
2) 文件操作函数
dir <names>: 取 <names> 中各单词的目录名, 最后一个 "/" 之前的字串, 如果没有 "/", 则返回 "./"
notdir <names>: 取 <names> 中单词的档案名
suffix <names>: 取 <names> 中单词的副档名, 若无副档名返回空字串, 如 $(suffix a.c) 返回 ".c"
basename <names>: 取 <names> 中单词的前缀部分, 若无前缀返回空字串
addsuffix <suffix> <names>: 将 <names> 中的单词加上后缀 <suffix>
addprefix <prefix> <names>: 将 <prefix> 加在 <names> 中的单词之前
join <list1> <list2>: 将 <list1> 中的单词与 <list2> 中的单词分别连接, 如 $(join aaa bbb, 111 222) 返回 "aaa111 bbb222"
3) foreach
foreach <var> <list> <text>:
将 <list> 的单词取出以 <var> 在 <text> 表示再返回, 如下例: $files = "a.o b.o c.o d.o"
names := a b c d
files := $(foreach n, $(names), $(n).o)
4) call
call <expression> <arg1> <arg2>: 创建参数化的函数
reverse = $(1) $(2)
foo = $(call reverse, a, b)
则 $foo = "a b"
5) origin
origin <variable>: 告知 <variable> 是从哪来的, 其返回值有:
"undefined": 从没被定义
"default": 默认的的定义
"environment": 环境变量, 且 make 执行时, "-e" 没有被打开
"file": 定义在 makefile 中
"command line": 被命令行定义
"override": 被 override 指示符重新定义
'automatic": 自动化变量
origin 应用的示例如, 我们想判断 foo 这变量如果由环境而来, 则重新定义, 若是其他来源, 则不重新定义.
ifdef foo
ifeq "$(origin foo)" "environment"
foo = b, g, etc.
endif
endif
6) 控制 make 的函数
error & warning: error 会中断 make, warning 则不会, 用法都是一样.
error <text>: 输出错误讯息并中断 make
warning <text>: 输出警告不中断 make