分类: LINUX
2010-11-13 16:39:11
三、命令
规则的命令由一些Shell命令行组成,他们被一条一条的执行。
1、 命令回显
如果执行的命令行以字符@开始,则make在执行时这个命令就不会被回显。
使用make的命令行参数“-n”或“—just-print”来显示所要执行命令。
使用make的命令行参数“-s”禁止显示所要执行的命令。
2、 命令的执行
对于多行命令,make为每一行命令使用一个独立的子shell去执行。
所以需要注意:在一个规则的命令中,命令行“cd”改变目录不会对其后的命令的执行产生影响。就是说其后的命令执行的工作目录不会是之前使用“cd”进入的那个目录。如果要实现这个目的,就不能把“cd”和其后的命令放在两行来书写。而应该把这两条命令写在一行上,用分号分隔。这样它们才是一个完整的 shell 命令行。如:
foo : bar/lose
cd bar; gobble lose > ../foo
如果希望把一个完整的 shell 命令行书写在多行上,需要使用反斜杠(\)来对处于多行的命令进行连接,表示他们是一个完整的 shell 命令行。例如上例我们以也可以这样书写:
foo : bar/lose
cd bar; \
gobble lose > ../foo
3、 并发执行命令
GNU make 可以同时执行多条命令。在通常情况下,一个时刻只有一个命令在执行,下一个命令在当前命令执行完成之后才能够执行。可以通过make “-j”或”--job”,选项后接整型参数,告诉make在同一时刻允许执行命令的数目。
并行执行命令带来的问题:
A、 同一时刻多个命令执行输出,造成输出到终端的信息交替。很难根据信息来定位错误。
B、 在同一时刻存在多个命令读取标准输入,但对于标准输入来说,在同一时刻只能有一个进程访问输入设备,从而造成其他命令在读取输入时,因输入无效而导致错误。
C、 会使make的递归调用出现问题。
通过make “k”或”--keep going”选项在命令执行错误时不会被立即终止。而是到最后完成后续所有依赖的重建,在链接时才会出错。
通过make ”-I”或”—max-load”选项接浮点数来进行负荷限制,如果当前系统的负荷高于”-I”指定的值,那么make就不会在其他任务完成之前启动任何任务。缺省情况下没有负荷限制。
4、 命令执行的错误
当命令执行失败时,我们可以通过以下几个方法来忽略失败:
A、 在命令前加“-”来忽略此命令的执行失败
B、 通过make “-i”或者”—ignore-errors”来忽略所规则中所有命令的执行错误。
C、 make “-k” 或”--keep going”选项在执行命令出现错误时不立即退出,而是继续后续命令的执行。直到无法继续执行命令时才异常退出。通过”-k”选项,我们可以在对一个工程的多个文件进行修改后,用参数来帮助我们确认对那些文件的修改是否正确(正确的可以编译、不正确的不能编译)。
5、中断make的执行
Make在执行命令时如果收到一个致命信号(结束make),make将会删除命令重建的规则目标文件。其依据是此目标文件的当前时间戳是否和make开始的时间戳相同。(如果相同就不删除,如果比开始的时间“新”则会删除)。
6、make的递归执行
Make的递归调用指的是:在makefile中使用”make”作为一个命令来执行本身或者其他makefile文件。
在make的递归调用时,在makefile中规则的命令行应该使用变量”MAKE”来代替直接使用”make”。变量MAKE的值是/bin/make
export:指明makefile的变量可以传送到子makefile中。
二、文件名通配符
通配符的类型:“*”、“?”、“[….]”。
通配符使用的场合:
A:可以使用在目标文件,依赖文件中、此时make会自动展开
B:可以使用在命令中,展开是在shell执行命令时进行的。
通配符存在的缺陷:
例如:
object = *.o
foo:$(object)
gcc –o foo $(object)
如果在目录下存在*.o的文件列表,则make时不会出错。如果删除了目录下所有的*.o文件,则make时会出错。原因:应为没有.o文件的文件名,所以无法根据隐含规则生成.o的文件。
三、目录的搜寻
1、 一般搜索(变量VPATH)
VPATH指明文件的搜索路径(只有采用自动化变量时才有效,当采用实际的文件名时,必须带完整路径)。
当要搜索多个目录时,多个目录用:号分开 VPATH= ./ : ../。
2、选择性搜索(变量vpath)
使用有三种方法:
A:vpath PATTERN DIRECTORIES
为符合模式“PATTERN”的文件制定搜索目录“DIRECTORIES”,多个目录使用空格或冒号分隔开。
B:vpath PATTERN
清除前设置的符合模式PATTERN的文件搜索路径。
C:vpath
清除所有设置好的文件搜索路径。
3、目录的搜索机制
当采用前面介绍的两种搜索目录的方法搜索到完整的路径名,可能不是目标文件的依赖,此时搜索到完整路径名有可能需要废弃。Make在执行makefile文件时对文件路径保存或废弃所依据的算法如下:
A、首先,如果规则的目标文件在 Makefile 文件所在的目录(工作目录)下不存在,那么就执行目录搜寻。
B、如果目录搜寻成功,在指定的目录下存在此规则的目标。那么搜索到的完整的路径名就被作为临时的目标文件被保存。
C、对于规则中的所有依赖文件使用相同的方法处理。
D、完成第三步的依赖处理后,make程序就可以决定规则的目标是否需要重建,两种情况时后续处理如下:
a)规则的目标不需要重建:那么通过目录搜索得到的所有完整的依赖文件路径名有效,同样,规则的目标文件的完整的路径名同样有效。就是说,当规则的目标不需要被重建时,规则中的所有的文件完整的路径名有效。已经存在的目标文件所在的目录不会被改变。
b)规则的目标需要重建:那么通过目录搜索所得到的目标文件的完整的路径名无效,规则中的目标文件将会被在工作目录下重建。就是说,当规则的目标需要重建时,规则的目标文件会在工作目录下被重建,而不是在目录搜寻时所得到的目录。这里,必须明确:此种情况只有目标文件的完整路径名失效,依赖文件的完整路径名是不会失效的。否则将无法重建目标。
我们可以通过变量GPATH来改变目标文件存放的目录。
4、命令行和搜索目录
Make在执行时,通过目录搜索得到的目标的依赖文件可能会在其他目录(此时依赖文件的完整路径名),但是已经存在的规则命令却不能发生变化。因此,书写命令时我们必须保证当依赖文件在其他目录下被发现时规则的命令能够正确执行。处理这类问题的方式是通过使用自动化变量,因为使用自动化变量能得到完整的路径名。
5、隐含规则和搜索目录
隐含规则同样会为依赖文件通过指定搜索目录来进行搜索。隐含规则中的命令行就是通过使用自动化变量来解决目录搜索可能带来的问题,相应的命令中的文件名都是使用目录搜索得到的完整的路径名。
6、库文件和搜索目录
可以通过一个依赖文件名“-lNAME”来指定库文件名。Make将根据“NAME”首先搜索当前系统可提供的共享库,如果当前系统不能提供这个共享库,则搜索它的静态库(当然你可以在命令行中指定编译或者链接项来指定是动态链接还是静态链接)
搜索库文件的过程:
A、 make在执行规则时会在当前目录下搜索一个名字为libNAME.so的库文件
B、 如果在当前工作目录下不存在这样一个文件,则make程序会继续搜索使用“VPATH”或者“vpath”指定的搜索目录。
C、 如果还是不存在,make将搜索系统默认目录:/lib、/usr/lib、/usr/local/lib。
如果libNAME.so通过以上的途径最后还是没有找到的话,那么make程序将会按照以上的搜索顺序查找名字为:libNAME.a的文件。是由变量.LIBPATTERNS来指定。
四、静态模式
1、 静态模式的规则:规则存在多个目标,并且不同的目标可以根据目标文件的名字来自动构造出依赖文件
2、 静态模式的规则语法:
静态模式规则的基本语法:
TARGETS ...: TARGET-PATTERN: PREREQ-PATTERNS ...
COMMANDS
...
“TAGETS”列出了此规则的一系列目标文件。像普通规则的目标一样可以包含通配符。
“TAGET-PATTERN”和“PREREQ-PATTERNS”说明了如何为每一个目标文件生成依赖文件。从目标模式(TAGET-PATTERN)的目标名字中抽取一部分字符串(称为“茎”)。使用“茎”替代依赖模式(PREREQ-PATTERNS)中的相应部分来产生对应目标的依赖文件。下边详细说明这一替代的过程。
首先在目标模式和依赖模式中,一般需要包含模式字符“%”。在目标模式(TAGET-PATTERN)中“%”可以匹配目标文件的任何部分,模式字符“%”匹配的部分就是“茎”。目标文件和目标模式的其余部分必须精确的匹配。看一个例子:目标“foo.o”符合模式“%.o”,其“茎”为“foo”。而目标“foo.c”和“foo.out”就不符合此目标模式。每一个目标的依赖文件是使用此目标的“茎”代替依赖模式(PREREQ-PATTERNS)中的模式字符“%”而得到。例如:上边的例子中依赖模式(PREREQ-PATTERNS)为“%.c”,那么使用“茎”“foo”替代依赖模式中的“%”得到的依赖文件就是“foo.c”。需要明确的一点是:在模式规则的依赖列表中使用不包含模式字符“%”也是合法的。代表这个文件是所有目标的依赖文件。
我们来看一个例子,它根据相应的.c 文件来编译生成“foo.o”和“bar.o”文件:
objects = foo.o bar.o
all: $(objects)
$(objects): %.o: %.c
$(CC) -c $(CFLAGS) $< -o $@
例子中,规则描述了所有的.o文件的依赖文件为对应的.c文件,对于目标“foo.o”,取其茎“foo”替代对应的依赖模式“%.c”中的模式字符“%”之后可得到目标的依赖文件“foo.c”。这就是目标“foo.o”的依赖关系“foo.o: foo.c”,规则的命令行描述了如何完成由“foo.c”编译生成目标“foo.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
在使用静态模式规则时,指定的目标必须和目标模式相匹配,否则在执行make时将会得到一个错误提示。如果存在一个文件列表,其中一部分符合某一种模式而另外一部分符合另外一种模式,这种情况下我们可以使用“filter”函数来对这个文件列表进行分类,在分类之后对确定的某一类使用模式规则。例如:
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 $<
其中;$(filter %.o,$(files))的结果为“bar.o lose.o”。“filter”函数过滤不符合“%.o”模式的文件名而至返回所有符合此模式的文件列表。第一条静态模式规则描述了这些目标文件是通过编译对应的.c 源文件来重建的。同样第二条规则也是使用这种方式。
静态模式规则在一个较大的工程中非常有用的。它可以对一个工程中的同类文件的重建规则进行一次定义,而实现对整个工程中此类文件指定相同的重建规则。比如,可以用来描述整个工程中所有的 .o 文件的依赖规则和编译命令。通常的做法是将生成同一类目标的模式定义在一个make.rules 的文件中。在工程各个模块的 Makefile 中包含此文件。
七、双冒号规则
双冒号规则就是使用“::”代替普通规则的“:”得到的规则。当同一个文件作为多个规则的目标时,双冒号规则的处理和普通规则的处理过程完全不同(双冒号规则允许在多个规则中为同一个目标指定不同的重建目标的命令)。
首先需要明确的是:Makefile 中,一个目标可以出现在多个规则中。但是这些规则必须是同一种规则,要么都是普通规则,要么都是双冒号规则。而不允许一个目标同时出现在两种不同的规则中。双冒号规则和普通规则的处理的不同点表现在以下几个方面:
1. 双冒号规则中,当依赖文件比目标更新时。规则将会被执行。对于一个没有依赖而只有命令行的双冒号规则,当引用此目标时,规则的命令将会被无条件执行。而普通规则,当规则的目标文件存在时,此规则的命令永远不会被执行(目标文件永远是最新的)。
2. 当同一个文件作为多个双冒号规则的目标时。这些不同的规则会被独立的处理,而不是像普通规则那样合并所有的依赖到一个目标文件。这就意味着对这些规则的处理就像多个不同的普通规则一样。就是说多个双冒号规则中的每一个的依赖文件被改变之后,make 只执行此规则定义的命令,而其它的以这个文件作为目标的双冒号规则将不会被执行。
我们来看一个例子,在我们的 Makefile 中包含以下两个规则:
Newprog :: foo.c
$(CC) $(CFLAGS) $< -o $@
Newprog :: bar.c
$(CC) $(CFLAGS) $< -o $@
如果“foo.c”文件被修改,执行 make 以后将根据“foo.c”文件重建目标“Newprog”。而如果“bar.c”被修改那么“Newprog”将根据“bar.c”被重建。回想一下,如果以上两个规则为普通规时出现的情况是什么?(make 将会出错并提示错误信息)当同一个目标出现在多个双冒号规则中时,规则的执行顺序和普通规则的执行顺序一样,按照其在 Makefile 中的书写顺序执行。
chinaunix网友2010-11-15 19:31:21
很好的, 收藏了 推荐一个博客,提供很多免费软件编程电子书下载: http://free-ebooks.appspot.com