1、;targets是文件名,以空格分开,可以使用通配符。一般来说,我们的目标基本上是一个文件,但也有可能是多个文件。command是命令行,如果其不与“target:prerequisites”在一行,那么,必须以Tab键开头,如果和prerequisites在一行,那么可以用分号做为分隔。(见上) prerequisites也就是目标所依赖的文件(或依赖目标)。如果其中的某个文件要比目标文件要新,那么,目标就被认为是“过时的”,被认为是需要重生成的。这个在前面已经讲过了。如果命令太长,你可以使用反斜框()作为换行符。make对一行上有多少个字符没有限制。规则告诉make两件事,文件的依赖关系和
2、如何成成目标文件。一般来说,make会以UNIX的标准Shell,也就是/bin/sh来执行命令。三、在规则中使用通配符 如果我们想定义一系列比较类似的文件,我们很自然地就想起使用通配符。make支持三各通配符:“*”,“?”和“.”。这是和Unix的B-Shell是相同的。波浪号(“”)字符在文件名中也有比较特殊的用途。如果是“/test”,这就表示当前用户的$HOME目录下的test目录。而“hchen/test”则表示用户hchen的宿主目录下的test目录。(这些都是Unix下的小知识了,make也支持)而在Windows或是MS-DOS下,用户没有宿主目录,那么波浪号所指的目录则根据
3、环境变量“HOME”而定。通配符代替了一系列的文件,如“*.c”表示所以后缀为c的文件。一个需要我们注意的是,如果我们的文件名中有通配符,如:“*”,那么可以用转义字符“”,如“*”来表示真实的“*”字符,而不是任意长度的字符串。好吧,还是先来看几个例子吧:clean:rm-f*.o 上面这个例子我不不多说了,这是操作系统Shell所支持的通配符。这是在命令中的通配符。print:*.c lpr-p$?touchprint 上面这个例子说明了通配符也可以在我们的规则中,目标print依赖于所有的.c文件。其中的“$?”是一个自动化变量,我会在后面给你讲述。objects=上面这个例子,表示了,
4、通符同样可以用在变量中。并不是说*.o会展开,不!objects的值就是“*.o”。Makefile中的变量其实就是C/C+中的宏。如果你要让通配符在变量中展开,也就是让objects的值是所有.o的文件名的集合,那么,你可以这样:$(wildcard*.o) 这种用法由关键字“wildcard”指出,关于Makefile的关键字,我们将在后面讨论。四、文件搜寻 在一些大的工程中,有大量的源文件,我们通常的做法是把这许多的源文件分类,并存放在不同的目录中。所以,当make需要去找寻文件的依赖关系时,你可以在文件前加上路径,但最好的方法是把一个路径告诉make,让make在自动去找。Makefi
5、le文件中的特殊变量“VPATH”就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去找寻依赖文件和目标文件。如果定义了这个变量,那么,make就会在当当前目录找不到的情况下,到所指定的目录中去找寻文件了。VPATHsrc:./headers 上面的的定义指定两个目录,“src”和“./headers”,make会按照这个顺序进行搜索。目录由“冒号”分隔。(当然,当前目录永远是最高优先搜索的地方) 另一个设置文件搜索路径的方法是使用make的“vpath”关键字(注意,它是全小写的),这不是变量,这是一个make的关键字,这和上面提到的那个VPATH变量很类似,但是它更为灵活
6、。它可以指定不同的文件在不同的搜索目录中。这是一个很灵活的功能。它的使用方法有三种:1、vpathdirectories为符合模式的文件指定搜索目录。2、vpath清除符合模式的文件的搜索目录。3、vpath 清除所有已被设置好了的文件搜索目录。vapth使用方法中的需要包含“%”字符。“%”的意思是匹配零或若干字符,例如,“%.h”表示所有以“.h”结尾的文件。指定了要搜索的文件集,而则指定了的文件集的搜索的目录。例如:vpath%.h该语句表示,要求make在“./headers”目录下搜索所有以“.h”结尾的文件。(如果某文件在当前目录没有找到的话) 我们可以连续地使用vpath语句,以
7、指定不同搜索策略。如果连续的vpath语句中出现了相同的,或是被重复了的$ 上述规则等价于:-bigbigoutput -littlelittleoutput 其中,-$(substoutput,$)中的“$”表示执行一个Makefile的函数,函数名为subst,后面的为参数。关于函数,将在后面讲述。这里的这个函数是截取字符串的意思,“$”表示目标的集合,就像一个数组,“$”依次取出目标,并执于命令。七、静态模式 静态模式可以更加容易地定义多目标的规则,可以让我们的规则变得更加的有弹性和灵活。我们还是先来看一下语法:.target-patternprereq-patternscommands
8、targets定义了一系列的目标文件,可以有通配符。是目标的一个集合。target-parrtern是指明了targets的模式,也就是的目标集模式。prereq-parrterns是目标的依赖模式,它对target-parrtern形成的模式再进行一次依赖目标的定义。这样描述这三个东西,可能还是没有说清楚,还是举个例子来说明一下吧。如果我们的定义成“%.o”,意思是我们的集合中都是以“.o”结尾的,而如果我们的定义成“%.c”,意思是对所形成的目标集进行二次定义,其计算方法是,取模式中的“%”(也就是去掉了.o这个结尾),并为其加上.c这个结尾,形成的新集合。所以,我们的“目标模式”或是“依
9、赖模式”中都应该有“%”这个字符,如果你的文件名中有“%”那么你可以使用反斜杠“”进行转义,来标明真实的“%”字符。看一个例子:bar.o all:$(objects) $(objects):%.o:%.c $(CC)$(CFLAGS)$上面的例子中,指明了我们的目标从$object中获取,“%.o”表明要所有以“.o”结尾的目标,也就是“foo.obar.o”,也就是变量$object集合的模式,而依赖模式“%.c”则取模式“%.o”的“%”,也就是“foobar”,并为其加下“.c”的后缀,于是,我们的依赖目标就是“foo.cbar.c”。而命令中的“$”和“$”则是自动化变量,“$”表示
10、所有的依赖目标集(也就是“foo.cbar.c”),“$”表示目标集(也就是“foo.obar.o”)。于是,上面的规则展开后等价于下面的规则:foo.o bar.obar.c bar.c试想,如果我们的“%.o”有几百个,那种我们只要用这种很简单的“静态模式规则”就可以写完一堆规则,实在是太有效率了。“静态模式规则”的用法很灵活,如果用得好,那会一个很强大的功能。再看一个例子:filesfoo.elclose.o $(filter%.o,$(files):%.elc,$(files):%.elc:%.el emacsbatch-byte-compile%.o,$(files)表示调用Make
11、file的filter函数,过滤“$filter”集,只要其中模式为“%.o”的内容。其的它内容,我就不用多说了吧。这个例字展示了Makefile中更大的弹性。八、自动生成依赖性 在Makefile中,我们的依赖关系可能会需要包含一系列的头文件,比如,如果我们的main.c中有一句“#includedefs.h”,那么我们的依赖关系应该是:main.omain.cdefs.h 但是,如果是一个比较大型的工程,你必需清楚哪些C文件包含了哪些头文件,并且,你在加入或删除头文件时,也需要小心地修改Makefile,这是一个很没有维护性的工作。为了避免这种繁重而又容易出错的事情,我们可以使用C/C+编
12、译的一个功能。大多数的C/C+编译器都支持一个“-M”的选项,即自动找寻源文件中包含的头文件,并生成一个依赖关系。例如,如果我们执行下面的命令:-Mmain.c 其输出是:于是由编译器自动生成的依赖关系,这样一来,你就不必再手动书写若干文件的依赖关系,而由编译器自动生成了。需要提醒一句的是,如果你使用GNU的C/C+编译器,你得用“-MM”参数,不然,“-M”参数会把一些标准库的头文件也包含进来。gccmain.c的输出是:main.o:/usr/include/stdio.h/usr/include/features.h /usr/include/sys/cdefs.h/usr/includ
13、e/gnu/stubs.h/usr/lib/gcc-lib/i486-suse-linux/2.95.3/include/stddef.h/usr/include/bits/types.h/usr/include/bits/pthreadtypes.h/usr/include/bits/sched.h/usr/include/libio.h/usr/include/_G_config.h/usr/include/wchar.h/usr/include/bits/wchar.h/usr/include/gconv.h/usr/lib/gcc-lib/i486-suse-linux/2.95.3/
14、include/stdarg.h/usr/include/bits/stdio_lim.h -MMmain.c的输出则是:那么,编译器的这个功能如何与我们的Makefile联系在一起呢。因为这样一来,我们的Makefile也要根据这些源文件重新生成,让Makefile自已依赖于源文件?这个功能并不现实,不过我们可以有其它手段来迂回地实现这一功能。GNU组织建议把编译器为每一个源文件的自动生成的依赖关系放到一个文件中,为每一个“name.c”的文件都生成一个“name.d”的Makefile文件,.d文件中就存放对应.c文件的依赖关系。于是,我们可以写出.c文件和.d文件的依赖关系,并让make
15、自动更新或自成.d文件,并把其包含在我们的主Makefile中,这样,我们就可以自动化地生成每个文件的依赖关系了。这里,我们给出了一个模式规则来产生.d文件:%.d:set-e;$;$(CPPFLAGS)$.$;seds,($*).o*,1.o$,g$.$.$ 这个规则的意思是,所有的.d文件依赖于.c文件,“rm$”的意思是删除所有的目标,也就是.d文件,第二行的意思是,为每个依赖文件“$”,也就是.c文件生成依赖文件,“$”表示模式“%.d”文件,如果有一个C文件是name.c,那么“%”就是“name”,“$”意为一个随机编号,第二行生成的文件有可能是“name.d.12345”,第三行
16、使用sed命令做了一个替换,关于sed命令的用法请参看相关的使用文档。第四行就是删除临时文件。总而言之,这个模式要做的事就是在编译器生成的依赖关系中加入.d文件的依赖,即把依赖关系:转成:main.d于是,我们的.d文件也会自动更新了,并会自动生成了,当然,你还可以在这个.d文件中加入的不只是依赖关系,包括生成的命令也可一并加入,让每个.d文件都包含一个完赖的规则。一旦我们完成这个工作,接下来,我们就要把这些自动生成的规则放进我们的主Makefile中。我们可以使用Makefile的“include”命令,来引入别的Makefile文件(前面讲过),例如:sourcesinclude$(sou
17、rces:.c=.d) 上述语句中的“$(sources:.c=.d)”中的“.c=.d”的意思是做一个替换,把变量$(sources)所有.c的字串都替换成.d,关于这个“替换”的内容,在后面我会有更为详细的讲述。当然,你得注意次序,因为include是按次来载入文件,最先载入的.d文件中的目标会成为默认目标。书写命令 每条规则中的命令和操作系统Shell的命令行是一致的。make会一按顺序一条一条的执行命令,每条命令的开头必须以Tab键开头,除非,命令是紧跟在依赖规则后面的分号后的。在命令行之间中的空格或是空行会被忽略,但是如果该空格或空行是以Tab键开头的,那么make会认为其是一个空命令。我们在UNIX下可能会使用不同的Shell,但是make的命令默认是被“/bin/sh”UNIX的标准Shell解释执行的。除非你特别指定一个其它的Shell。Makefile中,“#”是注释符,很像C/C+中的“/”,其后的本行字符都被注释。一、显示命令 通常,make会把其要执行的命令行在命令执行前输出到屏幕上。当我们用“”字符在命令行前,那么,这个命令将不被make显示出来,最具代表性的例子是,我们用这个功能来像屏幕显示一些信息。echo正在编译XXX模块. 当make执行时,会输出“正在编译XXX模块.”字串,但不会输出命令,如果没有“”,那么,make将输出:echo
copyright@ 2008-2022 冰豆网网站版权所有
经营许可证编号:鄂ICP备2022015515号-1