makefile使用
来源:百度文库 编辑:神马文学网 时间:2024/04/19 10:59:32
1 makefile实际上是一个单行shell脚本,用make来调用。
2 makefile使用的命令解释器由SHELL这个变量所控制,make 启动时会从用户环
保导入所有的变量经作为make有变量,但不包括SHELL变量。
3 每个命令必须以跳格符(tab)开头在工作目标之后,凡是第一个字符为跳格的文本行一律被视为命令。@放在要执行的命令前,执行时不打印这个命令。
4 $符号跟开括号,函数名,空格后跟一列由逗号分隔的参数,最后用关括号结
束。例如 SOURCES = $(wildcard *.c) 这行会产生一个所有以 '.c' 结尾的文
件的列表,然后存入变量 SOURCES 里。
5 常用函数。
A, wildcard, 它有一个参数,功能是展开成一列所有符合由其参数描述的文
件名,文件间以空格间隔。 $(wildcard *.c)产生一个所有以 '.c' 结尾的文件
的列表.
B, patsubst, 它需要3个参数——第一个是一个需要匹配的式样,第二个表示用什
么来替换它,第三个是一个需要被处理的由空格分隔的字列. OBJS =$(patsubst
%.c,%.o,$(SOURCES)) 处理所有在 SOURCES 字列中的字(一列文件名),如果它
的 结尾是 '.c' ,就用 '.o' 把 '.c'取代.
C.notdir 去除路径。
6 规则分为三部分,A 工作目标, B 它的必要条件。 C 所要执行的命令。
工作目标是一个必须建造的文件或进行的事情,必要条件或依存对像是工作目标得以被成功创建之前,必须事先存在的那些文件,而所要执行的命令是必要条件成立时将会创建工作目标的那些shell命令。
7 -l
8 make 被调用时会自动编译其所找到的第一个工作目标,要想更改工作目标,请在命令行上指定工作目标的名称。
9 可以用反斜线平延续过长的文本行。
10 具体规则
A 工作目标与必要条件是一种依存关系,不是说必要条件一定用在编译工作目标的文件夹中。如:a.o 只是编译a.c时生成,但b.c有改动时要重新编a.o
a.o: a.c
a.o: b.c /*当b.c变化时要重编a.o*/
B 通配符。
模式出现在工作目标或必要条件时,是由make进行能配符的的扩展,不模式出现在命令中时,是由subshell进行扩展的动作。
C 假想工作目标。(1)总是标记为未更新,并且认make知道不应该像处理一般规则一样,从源文件平建立工作目标为名的文件。(2)可以经假想目标作为另一个工作目标的必要条件,可以让make在进行实际的工作目标之前调用假想工作目标。在假想工作目标加上打印,可以输出相应的打印信息。(3)标准的假想目标有以下:all,install,chean, distclean, TAGS, info, check..
D 空工作目标。(参见《GNU MAKE项目管理》25页)
E 变量。用$(variable-name)来扩展变量。变量名是一单字符时不用圆括号。自动变量有以下几个:
(1)$@ ,工作目标的文件名。
(2)$< 第一个必要条件。
(3)$? 时间戳在工作目标之后的所有必要条件,以空格隔开。
(4)$^ 所有必要条件,以空格隔开,文件名不重复。
(5)$+ 如同$^,但包括重名的文件。
(6)$* 工作目标主文件名。不能在模式规则以外使用这个变量。
F 模式规则, 所有内置规则都是模式规则的实例。模式规则中%等同于unix中的shell.可以通过make –print-data-base查看当前的makefile有哪些默认规则和变量。
(1).%.o:%.c
$(CC)–c –o $@ $<
这个内置规则是把.c编译成.o
(2) %: %.o
$(CC ) –o $@$<
G静态模式规则,可以认为是限定模式规则的应用中在特定的文件上。
$(OBJ):%.o:%.c
$(CC)–c –o $@ $<
%.o模式会匹配$(OBJ)中所列出的每个文件,并取出其主干部分,分别构成各个.o文件,这样就产生了工作目标和必要条件。
H 隐含规则
A 隐含规则不是模式规则就是后缀规则。当目标加入makefile只要不写要执得的脚本就可以使胜隐含规则了。
B 一个没有指定脚本的模式规则将会从make的规则库中删除相应的规则。
12 文件查找。
以VPATH和vpath来查找文件。
A .VPATH变量的内容是一份目录列表,可供make搜索其所用的文件,注意这个目录列表只是用来搜索工作目标以及必要条件,但不包括命令脚本中所提到的文件。
B. 如果多个目录中出现同名的文件,make只会取第三者个被找到的文件。
C. 可以用vpath指令在特定目录搜索特定文件。
vpath patterndirectory-list
vpath %.c src 在src目录下搜索.c 文件。
D.个人觉得小项目中可用这个功能,大项目中尽量不用,否则会导制难以发现的问题。
13 自动产生依存关系。(还要完善)
(1)一个C文件可能include多个头文件,手动去分析这些关系不太可能,所以要自动去分析。
gcc –M test.c这个命令可以打印出test.c 依赖什么头文件。
这样就可以编写一个脚本加入这些自动产生的脚本。但这样不最好的选择。
(2)第二种方法是为make加入一个include指令。
编写一个makefile工作目标,此工作目标的动作就是以-M选项对所有源文件执行gcc,并将结果存入一个依存文件中,然后重新执行make以便把刚才所产生的依存文件引入makefile,这样就可以触发所要的更新动作。
例:
depend: file_a.cfile_b.c file_c.c
$(CC) –M $(CPPFLAGS) $^ > $@
include depend
但还有问题,就是对源文件加入或移除依存关系时通常不会产生depend文件,这会造成无法重新编译源文件,整个工作又会变得一团乱。
(3)GNU make中可以使用一个或能和一个简单的算法来解决上面的问题。
算法:如果我们为每个源文件产生依存关系,将之存入相应的依存文件(扩展名为.d的文件)并以该.d文件为工作目标来加入此依存规则,这样,当源文件被改变时,make就会知道要更新该.d文件以及目标文件。
如:test.o test.d: test.c test.h filea.h
程序中可以使用如下模式规则用命令脚本。
%d: %c
$(CC) –M $(CPPFLAGS) $< > $@.$$$$; \
sed ‘s,\($*\)\.o[ :]*,\1.o $@ : , g’ <$@$$$$ > $@; \
rm –f $@.$$$$
14 管理程序库
程序库可简单的认为把相关的目标文件聚集在一起。
(1),ar rv libtest.a test1.o test2.o test3.o
r选顶表示要以指定的目标文件替换程序库里的成员。
v 选项表示ar要详细打印出它所做的动作。
(2),两种方法引用程序库。
A,在命令行上直接指定这个程序库全名。
gcc litest.a test3.o –otest.elf
B,使用-l选项。
gcc -ltest test3.o –o test.elf
尽量使用每二种,因为-l选项告诉gcc去搜索相应的目录。-L选项可以指定搜索路径,用于所有和程序库的搜索上.
(3),程序库会为它所包含的符号提供索引,较新的ar程序会在新成员加入时自动管理此索引,旧的ar不会这样做,所以对于旧的ar要用另外一个程序来修建或更新索引.
ranlib libtest.a
(4),程序库有可能有相互依赖关系,如:A中用到B, B里又用到A,但A要放在B前面,这样在用这些程序库时应这样.
–lA –lB –lA
15 变量与宏.
(1), 要取一个变量的值应这样$(var),或${var}注意在shell中不能用括号.
(2), 给变量赋值时注意,赋值号右边字特串前导空格会被删除但字符串后面的空格会被保留,这有时会导致问题,所以不要在后面加空格。变量主要有三种:常量,内部变量,函数。
(3)简单扩展变量:在make从makefile读进该变量的定放语句,赋值运算符右边的部分会立刻扩展。
CC :=gcc
MAKE_DEPEND := $(CC) –M 注意如果CC没有定义那么会被扩展成
MKDIR :=mkdir –p
(4)递归扩展变量:扩展动作会被延迟到该变量被使用时才进行扩展,有可能出现令人意外的结果。
source = *.c
objects = $(subst .c, .o, $(source))
(5)条件赋值:在变量值尚不存在的情况下进行变量的赋值的动作。
CC ?= gcc
(6)附加运算符:将文本附加到变量里。
VAR += test.c
(7)封装命令序列,也可以称为宏
define create-jar
….
endef
(8)函数变量
maybe-make-dir = $(if $(wildcard $1), , $(MKDIR) $1)
(9)何时扩展变量
make运行时分两个阶段:A 读进makefile以及被引入的其它makefile和规则文件,其中所定义的变量和规则会被加载进make的内部数据库,而且依存图也建立起来。B make分析依存图并且判断要更新的工作目标,然后完成相应的动作。
16 变量来自何处
(1)文件中,定义在makefile或是被makefile引入的文件。
(2)命令行。可以在命令行上定义或重新定义变量。命令行上的变量值将会覆盖掉环境变量以及makefile文件中的赋值结果。
(3)环境。
(4)自动创建。make会在执行一个规则的命令脚本前立刻创建自动变量。
17 条件指令与引入指令
(1)条件指令用来控制makefile文件中使用哪些部分,省略哪些部分。
if-condition
…….
endif
或
if-condition
…….
else
…….
endif
ifcondition可以是以下之一,
ifdef variable-name
ifndef variable-name
ifeq test
ifneq test
进行条件测试时不应该以$()括住variable-name,可以如下表示。
ifeq‘var1 var
(2)注意条件指令前不能以一个跳格符开始,如果以跳格符开始的话
(3)include指令
makefile可以用include指令引入其它文件,这个功能通常会用来引入make头文件中所存放的共同的make定义,或是用来自动产生依存关系信息。
18 命令
(1)每个命令会在它自己的shell中执行,所以要让一系列shell命令一起执行,要特别处理。简单的说就是把命令放在一行进行处理。
.INTERMEDIATE:file_list
file_list:
for d in logic ui; \
do \
echo $$d/*.java: \
done > $@
19 命令修饰符
(1)@ 不要输出命令
可以这样用
QUIET=@
script:
$(QUIET)cmd
这样只用控制QUIET变量就可以控制命令输出了。
(2)- 破折号前缀,忽略命令中的错误。
(3)+加号修饰符, 要求make执行命令就算用户以—just-print命令来执行。
20 错误与中断
(1)make 每执行一个命令就会返回一个状态码,零表示成功,非零表示出错。常见的有如下:
Ashell的if 结构没有使用else当走else分支时会返回错误。
Brm , mkdir等命令。rm –f ,mkdir –P
(2)如查用户要求脚本中的两条语句在同一个subshell中执行,那长必须用语句连接符来隔开它们(;或&&).
21 大型项目管理
现有两种方法:递归式和单一makefile从每个组件目录引入信息。
(1)递归makefile
A 要使用make来编译一个大型项目时,可以为每一个目录编写一个简单的,各自独立的makefile,然后分别地执行它们。有以下两种进入下层的目录。
make –directory=subdir
make –C subdir
B 假设一个项目有三个库,UI, DECODE, DATABASE, 一个可执行程序PLAYER,它们分别放在不同的目录里。
可以这样写makefile
UI_DIR
DECODE_DIR
DATABASE_DIR
PLAYER_DIR
SUB_DIR=$(UI_DIR)$(DECODE_DIR) $(DATABASE_DIR) $(PLAYER_DIR)
for dir in$(SUB_DIR)
do
make –C $$dir
done