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,形式的必要条件出现时,make 会搜索libNAME.so文件,如果没有会再找libNAME.a文件,找到后就进行最后的动作----链接。

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没有定义那么会被扩展成  -M

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  var2’?ifeq“var1”‘ var2’?ifeq‘  var2’“var1”

(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