×××××××××××××××××××××××××××××××××××××
××× 呕心整理,转载请注明出处 GuoSenZQ ×××
×××××××××××××××××××××××××××××××××××××
语法
TARGETS:PREREQUISITES (; COMMAND)
COMMAND
...
TARGETS : 目标,可以是空格分开的多个文件,也可使用通配符;
PREREQUITITES : 依赖,根据依赖更新后是否需要重建目标,将依赖分为两种,用“|”进行分割,“需要|不需要”,把更新后不需要重建目标的依赖称为“order-only”依赖;
COMMAND : 有两种风格,可以与目标和依赖在同一行,用分号分割,或者在下一行,并必须以【Tab】字符开始;
目录搜索
VPATH:是个变量,指定了make执行时当前Makefile中所有文件的搜索路径,包括规则的依赖文件和目标文件,定义时通过冒号“:”将多个目录分开,如LIBS_DIRS = /lib:/usr/lib:/usr/local/lob;
vpath:是个关键字,可以为不同类型的文件指定不同的搜索路径,使用时通过模式字符“%”进行匹配,但是模式匹配所指定的文件仅仅指在Makefile中出现的相应类型的文件,(如:%.h 并不包含源文件中包含的头文件所在的路径,其需要在编译时通过 gcc 的 -I 选项进行指定),使用格式如下,为所有符合模式“PATTERN”的文件指定搜索目录“DIRECTORY”,多个目录使用空格或冒号分开 :
vapth PATTERN DIRECTORIES;
GPATH:变量,如果目标是通过目录搜索得到的完整路径名,且该目标所在的目录出现在GPATH所定义的目录列表中,那么重建目标时将在该目录下进行,而不是在Makefile所在的目录下进行;
-Iname : 若在依赖文件列表中存在这种格式的依赖文件,make将根据name在当前系统中依次搜索可提供的共享库、静态库。
搜索时的搜寻顺序是: Makefile文件所在的目录 => VPATH、vpath指定搜索目录 => 对于用-I方式引入的库文件依赖,还会搜索库文件的默认目录/lib、/usr/lib、PREFIX/lib;
通过搜索得到的文件名可能是包含路径信息的完整文件名,可能并不是规则中列出的文件名,为了保证规则中能正确的使用依赖文件,规则的命令行中必须使用自动化变量来代表依赖文件;
特殊目标
(0). 特殊的普通目标
clean:
rm *.o temp
特点:
i). 规则的目标不是一个存在的文件;
ii). 没有依赖或命令;
可能产生的问题:
i). 目标名字可能与当前工作目录下的实际文件名字冲突。
(a). 若目标名字与当前工作目录下的实际名字不冲突时,即此时当前工作目录下不存在同名的文件,且该目标没有依赖,此时make便会通过执行命令去“完成目标的创建”(创建和更新目标是规则的命令部分被赋予的理论上应有的功能),所以此时该规则的命令会被成功执行;
(b). 若冲突时,由于规则没有任何依赖,且目标的同名文件已存在,所以目标会被认为是最新的,因此该规则的命令将不会被执行,此时的结果便与我们定义该规则的初衷相悖;
ii). 当目标名字与当前工作目录下的实际文件名不冲突时,及目标的同名文件不存在时,make在执行时会试去查找可用的隐含规则来创建它,虽然最终并不会找到合适的隐含规则去创建,但是试图查找可用的隐含规则的过程是不可避免的,这会影响make的执行效率;
(1). 强制目标:
特点:
i). “FORCE” 是一个强制目标,但是在GNU make中应避免使用,尽量通过 .PHONY 的方式来代替;
ii). 强制目标本身不是一个真正的文件,并且规则没有命令或依赖,该规则中的目标总被认为是最新的(无论强制目标的同名文件是否存在,没有依赖的规则的目标文件总被认为是最新的,但规则的命令是否会被执行取决与同名文件则是否存在)
iii). 当强制目标作为某个规则的依赖时,由于依赖总被认为是最新的,则以强制目标作为依赖的规则中所定义的命令总会被执行,因此可使用强制目标作为上述(0)中的特殊的普通目标的依赖,来达到与定义成伪目标同样的效果,如下:
clean:FORCE
rm *.o temp
(2). 伪目标
伪目标不是一个真正的文件名,可以没有命令或依赖,也可以都有,通过指定规则的目标来完成某一功能的一系列命令,通过 .PHONY 进行定义,如下:
.PHONY:clean
clean:
rm *.o temp
特点:
i). 避免Makefile中的目标和工作目录下的实际文件名冲突;
ii). 提高make执行效率,对于声明为伪目标的规则,make执行时不会试图去为它查找隐含规则来创建它;
iii). 无论目标文件是否存在,其规则中的命令总会被无条件执行。一般不将伪目标作为一个目标的依赖,因为该规则每次在检查时,伪目标的命令都会被执行;
iv). 伪目标可以有自己的依赖(可以是一个或者多个文件或伪目标)。如果一个伪目标作为另外一个伪目标的依赖时,作为依赖的伪目标会被认为时目标伪目标的子例程来处理,即其时目标伪目标必须要执行的部分;
v). 伪目标可以实现并行处理,而替代shell语句中的for语句(它是顺序执行);
(3). 空目标文件
是伪目标的一个变种,这个目标可以使一个存在的文件,但文件的内容我们并不关心,它只是用来记录上一次执行此命令的时间,通常是在规则的所有命令行的最后使用touch命令来更新目标文件的时间戳,以记录此规则命令的最后执行时间,如下:
print : foo.c bar.c
lpr -p $?
touch print (print 文件存在,但其只是为了记录规则最近执行的时间戳)
多规则目标 与 多目标规则
(1). 一个文件可以作为多个规则的目标,但多个规则中只能有一个规则定义命令,即重建此目标的规则只能出现一个规则中,否则使用最后一个规则中定义的命令,并同时提示错误。但若目标的任何一个规则都没有定义重建此目标的命令,make会寻找合适的隐含规则来重建此目标。某些情况下,需要对相同的目标使用不同的规则中定义的命令,需要使用“双冒号”规则来实现,具体此处不表,详细见下文;
(2). 普通的多目标规则,是指在一个规则中有多个目标(此时的多个目标是通过将多个目标的名字具体的显式的给出,而非通过模式字符给出一个固定的模式如:foo.o bar.o: defs.h 而非 *.o: defs.h),该规则在处理时,将每一个目标作为一个独立的规则来处理,所以多个目标就对应多个独立的规则,且各个规则都有各自的命令行,只不过各个规则的命令行可能相同(此处区别于模式规则中的多目标规则);
(注:注意区分符号 “%”与“ * ”,“%”是模式字符,用在模式规则中,包括静态模式规则中,而" * "是通配符,在任何规则中均可使用)
模式规则
(1). 模式规则类似于普通规则,只是在模式规则中,目标名中需要包含有模式字符“%”,包含有模式字符“%”的目标被用来匹配一个文件名,“%”可以匹配任何非空字符串。模式规则中依赖文件中可以不包含模式字符“%”,其含义是所有符合目标模式的目标文件都依赖于指定的文件;
%.o:%.c
$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
(2). 一个模式规则可以存在多个目标,多目标的模式规则和普通的模式规则不同:普通的多目标规则处理是将每一个目标作为一个独立的规则来处理,所以多个目标就对应多个独立的规则;对于多目标模式规则来说,所有的目标共同拥有依赖文件和规则的命令行,即当文件符合多个目标模式中的任何一个时,规则定义的命令行就有可能会被执行,且执行后,规则不会再去检查是否需要重建符合其它模式的目标,表明多目标的模式规则在make时是被作为一个整体来处理的,如下: (该规则是一个隐含模式规则);
%.x: CFLAGS+=-g
%.o: CFLAGS+=-O2
%.o %.x: %.c
$(CC) $(CFLAGS) $< -o $@
(在执行 make foo.o foo.x 时,只会有 foo.o被创建了,同时提示foo.x是最新的,但其实foo.x并没有被创建)
注:
i). 模式字符“%”的匹配和替换发生在规则中所有变量和函数引用展开之后,变量和函数的展开一般发生在make读取Makefile时,模式规则中的“%”的匹配和替换则发生在make执行时;
ii). 当一个目标文件同时符合多个不同的目标模式时,make将会把第一个目标匹配的模式规则作为重建它的规则;
iii). Makefile中指定的模式规则会覆盖隐含模式规则,即使用时明确规则永远优先于隐含规则;
iv). 当目标模式中包含斜杠(目录部分)【特别提醒一点,在目标模式中是可以填加目录信息的,在实际中非常有用】,在进行目标文件匹配时:文件名中包含的目录字符串在匹配之前被移除只进行基本文件名的匹配;匹配成功后,再将目录部分加入到匹配之后的字符串之前形成“茎”; 依赖文件的产生: 首先使用“茎”中的非目录部分替代依赖文件中的模式字符“%”,之后再将目录部分加入到形成的依赖文件名之前,构成依赖文件的全路径名。如模式规则: e%t: c%r,则目标 src/eat 对应的茎为 “src/a”,对应的依赖应是 “src/car” ;
静态模式规则
静态模式规则: 规则存在多个目标,并且不同的目标文件可以根据目标的名字来自动构造出依赖文件,而所谓静态的是指多目标所指的具体目标数量是已知的且各个目标的名字也是已知的(当然这些目标可以是在Makefile中明确定义的,也可以是通过扫描或过滤函数获得)。使用从目标模式中抽取的部分字符串来替代依赖模式中的相应部分来产生对应目标的依赖文件。
TARGETS( ...):TARGET-PATTERN:PREREQ-PATTERNS (...)
COMMANDS
......
TARGETS : 目标,可以是多个
TARGET-PATTERN : 目标模式
PREREQ-PATTERNS: 依赖模式(依赖模式中也可以不包含模式字符“%”,表示显示指明的依赖文件是所有目标共同的依赖)
使用静态模式规则时,指定的目标必须和目标模式相匹配,否则执行make时将产生错误提示,此时可以使用filter函数来提前对文件进行分类过滤。如:
objects = foo.o bar.o
all: $(objects)
$(objects):%.o:%.c
$(CC) -c $(CFLAGS) $< -o $@
隐含规则
(1). 隐含规则为make提供了重建一类目标文件的通用方法,隐含规则被用在任何可以和他匹配的目标上,如Makefile中没有为这个目标指定具体的规则,或者存在规则但没有命令行来指定重建操作,或者目标的依赖文件可以被搜索到,均可使用隐含规则来重建目标,当存在多个隐含规则和目标模式相匹配时,只执行其中的一个规则(取决于规则的定义顺序);
i). 隐含规则和静态模式规则都是用目标模式和依赖模式来构建目标的规则中的文件依赖关系,区别在于make在执行时使用他们的时机不同,隐含规则被用在任何可以和他匹配的目标上,而静态模式规则只能用在规则中明确指出的那些目标文件的重建过程;
ii). 给目标文件指定明确的依赖文件并不会影响隐含规则的搜索(只要规则中不包含命令,依然会被使用隐含规则来重建);
iii). 当不想让make为一个没有命令行的规则中的目标搜索隐含规则时,我们需要使用空命令来实现【见“命令”文档】;
iv). 可以使用“-r”或“–no-builtin-rules”命令行参数来取消内嵌的隐含规则,或者使用“-R”或“–no-builtin-variables”命令行参数来取消make的隐含变量,“-R”同时打开“-r”,因为没有了隐含变量隐含规则也将失去意义;
v). 在gcc编译源文件时,如果没有指定“-c”选项,gcc会在编译完成之后调用“ld”链接称为可执行文件;
vi). 隐含规则中所使用的(隐含)变量都是预定义的,分为两类:代表一个程序的名字,代表执行这个程序使用的参数;
vii). 如果一个目标需要一系列隐含规则才能完成它的创建,我们就把这个系列称为一个链。在make过程中,会自动将重建目标过程中需要依赖的中间过程文件加入到依赖关系列表中,且中间过程文件和那些明确指定的文件在规则中的地位完全相同,但是中间过程文件在make执行结束后会被删除;
viii). 默认的,在Makefile中明确提及的所有文件都不被作为中间过程文件来处理,但是在Makefile中可以使用特殊目标 “.INTERMEDIATE” 来指定将哪些文件作为中间过程文件来处理,即使这些文件在Makefile中被明确提及。此外,也可以明确指出希望保留的中间过程文件,方法时使用特殊目标“.SECONDARY”来指出这些文件,或者将这些文件作为特标“.PRECIOUS”的依赖,来取消在make重建目标终止时,对该目标执行删除动作;
ix). 同一个隐含规则在一个链中只能出现一次,否则将会导致无限循环的问题;
x). make的隐含规则表中,所有可用的优化规则处于首选地位;
(2). 修改内嵌隐含规则
i). 重建内嵌隐含规则时,需要使用相同的目标和依赖模式,命令可以不同,这样具有相同目标和依赖模式的内嵌规则将被新定义的规则替代;
ii). 取消内嵌隐含规则时,需要定义一个和隐含规则具有相同目标和依赖的规则,但这个规则没有命令行;
(3). 隐含规则搜索算法
万用规则
当模式规则的目标只是一个模式字符“%”时(他可以匹配任何文件名),称为万用规则,万用规则在书写Makefile时非常有用,但他会影响make的执行效率,因为make在执行时将会使用万用规则来试图重建其他规则的目标和依赖文件。若存在万用规则,make会对Makefile中提及的文件通过所有可能的隐含规则来创建它,即使这个文件已经是源文件,虽然最后这些文件不可能被创建,但该过程依然会进行,导致make执行效率会很低。
为了避免万用规则带来的效率低下的问题,对万用规则加以限制,有两种方式,在定义时要么将规则定义为最终规则;否则若不进行指定,那么该万用规则就是非最终的万用规则,在定义万用规则时,必须选择其中一种方式,如下:
(1). 将万用规则时将其设置为最终规则,定义时使用双冒号规则。作为最终规则,此规则只有在它的依赖文件存在时才能被应用【必须有依赖文件吗?与缺省规则怎么区别?】,即使它的依赖可以由隐含规则创建也不行;
i). 最终万用规则通过双冒号来定义,非最终的仍使用单冒号来定义;
ii). 最终的万用规则与普通的双冒号规则区别:最终的万用规则其目标是字符%,而对普通的双冒号规则,其目标与普通规则相同;
iii). 最终的万用规则优先级的优先级:一个目标在重建时各个规则的优先级为:明确指定的规则 > 万用规则 > 隐含规则 > 默认规则,
(2). 如果万用规则没有被定义为最终规则,那么它就是一个非最终规则,需要定义一个特殊的内嵌哑模式规则给出如何重建某一类文件,避免使用非最终的万用规则通过隐含规则进行无意义的尝试创建操作。内嵌哑模式规则格式:没有依赖,也没有命令行,在make的其它场合被忽略。我们可以为所有的 make 可识别的后缀创建一个哑模式规则。此外,非最终的万用规则不会被用来创建那些符合某一明确模式规则的目标和依赖文件。即是说如果在Makefile中对一个文件存在匹配的模式规则(非万用规则)和非最终的万用规则,那么对这个文件其重建时只会是它所匹配的这个模式规则,而非这个非最终的万用规则。这样做是为了避免make执行时使用非最终的万用规则来重建源文件情况的发生;
注:
i). 非最终的万用规则是使用“:”定义的普通规则;
ii). 对于内嵌哑模式规则,其只在非最终的万用规则下才有意义,对最终的万用规则,由于对诸如源文件的文件,其本身就不会被调用隐含规则进行重建,因此内嵌哑模式规则定义了也没有意义;
iii). 非最终的万用规则也可以用来实现Makefile的重载;
缺省规则
当make在执行过程中无法为一个文件找到合适的重建规则时(即Makefile中没有给出重建它的明确规则,同时也没有合适可用的隐含规则),那么make就使用这个规则来重建它。就是说,当需要创建的目标文件没有可用的命令时,就执行这个缺省规则;
(1). 定义缺省规则,可以使用最终的万用规则,如下所示。例如当我们调试Makefile时(可能一些源文件还没有完成),我们关心的是Makefile中所有的规则是否可以正确执行,而源文件的内容却不需要关心,基于这一点我们可以使用(和源文件同名的)空文件在Makefile中定义如下规则:
%::
touch $@
则在执行make过程中,对所有不存在的.c文件,将会使用 “touch” 命令创建一个空的源文件;
(2). 定义缺省规则的方式也可以使用伪目标 “.DEFAULT”,对如上的情况可以使用如下方式来代替:
.DEFAULT:
touch $@
对于没有指定命令行的伪目标 “.DEFAULT”,其含义是取消前面所有使用“.DEFAULT” 指定的缺省执行命令,同样也可以通过给 .DEFAULT 定义空命令的方式,让这个缺省规则不执行任何命令;
(3). 缺省规则也可以用来实现在一个Makefile中重载另一个Makefile文件
注: 没有指定命令行的规则和空命令规则功能是不同的,对于没有指定命令行的规则,make执行时会为其目标文件查找可用的隐含规则来进行重建;而空命令规则,是含有命令行的,只不过命令行为空,make执行时,不会为其调用隐含规则来进行重建;
双冒号规则
普通规则使用单冒号“:”定义,而双冒号规则使用双冒号“::”定义,它允许在多个规则中为同一个目标指定不同的重建目标的命令(即多规则目标)。
特性:
i). 对于同一个目标其规则只能是一种,要么全是单冒号的普通规则,要么全是双冒号规则,不予许同时存在两种规则;
ii). 对于没有依赖的只有命令行的双冒号规则,当引用此目标时,该规则的命令行将会被无条件执行(与伪目标类似)但是普通规则下,目标永远被认为时最新的,命令永远不会被执行;
iii). 一个目标有多个双冒号规则时,其多个规则的依赖并不会合并,每个依赖文件被改变后,make仅会执行此依赖所在规则中命令,其它的规则并不会被执行;
iv). 当同一个目标出现在多个双冒号规则中时,规则的执行顺序与普通规则一样,按照其在Makefile中的书写顺序执行;
v). 一般双冒号规则都要定义命令,如果一个双冒号规则没有定义命令,在执行规则时将为其目标自动查找隐含规则;
后缀规则
后缀规则是一种古老的定义隐含规则的方式,在新版本中使用模式规则作为对它的替代,模式规则相比后缀规则更加清晰明了,在当前版本中保留它的原因是为了能够兼容旧的Makefile文件。后缀规则有两种类型:双后缀和单后缀
i). 双后缀规则定义一对后缀,依赖文件的后缀和目标文件的后缀;单后缀规则只定义一个后缀,此后缀是源文件名的后缀它可以匹配任何文件;
ii). 判断一个后缀规则时单后缀还是双后缀,是通过判断规则的目标中可被识别的后缀的数量,若是仅有一个就是单后缀,两个的话就是双后缀;
iii). 一个后缀规则中不存在任何依赖文件,否则将被作为一个普通规则来对待;
iv). 一个没有命令行的后缀规则是没有任何意义的,达不到取消后缀规则的目的,后缀规则的作用仅仅是将规则加入数据库中;
v). 可识别的后缀是指特殊目标 “.SUFFIXES” 所有依赖的名字,通过给特殊目标 .SUFFIXES 添加依赖来增加一个可被识别的后缀,如" .SUFFIXES: .hack .win",其作用是将后缀 .hack 和 .win加入可识别后缀列表。此外可识别后缀列表支持被重置,方法是通过一个没有依赖的 .SUFFIXES 特殊目标来实现,然后再通过该特殊目标依次添加想要定义的可识别后缀即可;
自动产生依赖
(1). 在Makefile中需要书写.o文件与头文件间的依赖关系,而现代的C编译器提供了通过查找源文件中的 “#include”来自动产生这种依赖关系的功能,通过使用GCC的 “-M”选项来实现,而此时 “gcc -M” 的选项其输出结果中也包含对标准头文件的依赖关系描述,可以通过 “-MM” 来实现去掉依赖关系中的标准库头文件。
(2). 在旧版本的Makefile中,使用编译器此功能的通常做法是在Makefile中定义一个伪目标“depend”,来定义自动产生依赖关系文件的命令,先使用 make depend 来生成该depend文件,然后在 Makefile 中使用 include指示符包含这个文件。
(3). 在新版本中的 make 中,推荐的方式是为每一个源文件产生一个描述其依赖关系的 makefile 文件,对于一个源文件 NAME.c 其对应的描述依赖关系的 makefile 文件为 NAME.d,即 NAME.d 中描述了 NAME.o 所要依赖的所有头文件。
makefile 文件的重建
(1). include (关键字)告诉 make 暂停读取当前的 Makefile,转去读取 include 指定的一个或多个Makefile,完成以后在返回继续读取当前的 Makefile
(2). makefile 文件的相互包含使用关键字 “include”,“-”符号作用来忽略操作的错误,让make继续执行,考虑与其它make的兼容,可以使用 “sinclude” 来代替 “-include”
(3). 当环境中定义了 “MAKEFILES” 环境变量,make执行时首先将此变量的值作为需要读入的Makefile文件,作用与使用 include 类似,但该环境变量中的目标不会作为make执行的终极目标,且如果其中定义的文件无法找到也不会产生错误,该变量主要用在 make 的递归调用中的通信,实际使用中很少定义该变量
(4). make在执行时,要读取多个Makefile文件时,在对文件进行解析执行之前都会将变量追加到“MAKEFILE_LIST”中,即该变量中的最后一个值即是当前正在处理的Makefile文件名
(5). 在实际应用中,我们会明确给出Makefile文件,而并不需要make自动重建它们,但是make每次执行时总会自动的尝试重建那些已经存在的Makefile文件,如果需要处于效率考虑,可以为Makefile文件定义一个以Makefile文件为目标的空命令规则,来避免make查找重建Makefile的隐含规则。
(6). 如果使用一个没有依赖的只有命令的双冒号规则去更新Makefile文件(Makefile文件作为规则的目标),那么执行make时,这个Makefile文件总会被无条件更新,从而使得make的执行陷入一个死循环,为了防止这种情况的发生make在遇到一个目标是Makefile文件的双冒号规则时,将忽略对这个规则的执行。
(7). 对于Makefile文件中存在以Makefile文件为目标的规则,如果在使用 -t, -n, -q等命令来仅仅更新时间戳时,对于重建Makefile文件的规则是无效的,即这些规则任然会被执行,即Makefile文件任然会被重建,如果想要避免这种结果,不重建Makefile文件,可以在执行make的命令行中,将Makefile文件作为最终目标,则Makefile中更新Makefile的规则也不会被执行。
(8). 对于需要互相包含的两个Makefile文件,若两个文件中存在相同的目标但规则命令不同的规则时,将会产生错误,那么解决方法是通过万用规则来实现重载,即在万用规则的命令行中去执行需要包含的另一个Makefile文件。
(9). make如何解析Makefile文件,分两个阶段
i). 读取所有的Makefile文件,内建所有的变量,明确规则和隐含规则,建立所有的目标和依赖之间的依赖关系结构链表
ii). 根据第一阶段建立的依赖关系结构链表来决定哪些目标需要更新,并使用对应规则进行重建目标;对于变量和函数,如果在make执行的第一阶段被展开,那么称展开时“立即”的,其它的展开被称为时“延后”的,即这些变量和函数时直到后续某些规则需要使用时,或者在make处理的第二阶段他们才会被展开
通配符使用问题
(1). 在Linux 中我们所提到的通配符一般包含"*"(多字符),"?"(单字符),"[]"(过滤),而"%"称为模式字符【在SQL中它也被称为通配符】;
(2). 在Makefile中通配符采用Linux中的用法。Makefile中的通配符可以出现在以下两种场合:
i). 在规则的目标和依赖中,由make在读取Makefile时对其进行展开匹配处理
ii). 在规则的命令行中,由shell在执行命令时进行匹配处理
在除此以外的Makefile其它上下文中,不能使用通配符(比如变量定义、函数的引用等场合),否则会出现非预期的结果,解决方法是使用make定义的内嵌函数"wildcard"来完成同样的功能;
使用 make 更新静态库文件
(1). 静态库文件是一个".o"文件的集合,在Linux中使用"ar"工具对它进行管理;
(2). 库文件中的多个".o"成员,可独立的作为一个规则的目标,其书写规则为:ARCHIVE(MEMBER),且如果在规则中需要同时指定多个成员,可以将多个成员罗列在括号内,在描述库的多个成员时也可以使用shell通配符,这种方式只能出现在规则的目标和依赖,因为绝大所属命令不支持这种语法,且含有中这种表达式的规则的命令行只能是"ar"或其它可以对库成员进行操作的命令;
(3). 在静态库文件的规则中,使用"$%“来表示静态库的成员,而且静态库中所有的成员是不包含目录描述的,只有成员的名字,但是在书写静态库的规则时,可以使用包含路径信息的文件名,对于这样的目标重建它的规则的命令行中可能就需要使用自动化变量”%D","%F";
(4). 更新静态库的索引表,在我们编译完成的".o"文件直接加入到库的末尾后,却并没有更新库的有效符号表,连接程序链接时无法定位刚刚加入到库中".o"文件中定义的函数或变量,这就需要"ranlib"工具来对静态库的符号索引表进行更新,在Makefile中通常可如下操作,功能是在libfoo.a中加入成员"x.o"、"y.o"后更新静态库libfoo.a的符号索引表:
libfoo.a: libfoo.a(x.o) libfoo.a(y.o) ...
ranlib libfoo.a
但 GNU 版本的"ar"工具就不需要这一步了,它本身已经提供了在更新库的同时更新了符号索引表;