Makefile编写规则(二)文件搜索和隐含规则
上篇文章介绍了Makefile中通配符的使用以及变量的定义和使用,本文继续介绍Makefile的其他规则
Makefile目标文件搜索
工程文件中的源文件有很多,并且存放的位置可能不相同(工程中的文件会被放到不同的目录下),所以按照之前的方式去编写 Makefile 会有问题。之前的例子所有文件都和Makefile在同一个路径下,只要依赖文件存在就不会有问题。如果需要的文件是存在于不同的路径下,就用到了 Makefile 中为我们提供的目录搜索文件的功能。
常见的搜索的方法的主要有两种:一般搜索VPATH
和选择搜索vpath
。VPATH 和 vpath 的区别:VPATH 是变量,更具体的说是环境变量,Makefile 中的一种特殊变量,使用时需要指定文件的路径;vpath 是关键字,按照模式搜索,也可以说成是选择搜索。搜索的时候不仅需要加上文件的路径,还需要加上相应限制的条件。
1)、VPATH的使用
在Makefile可以这样写:可以这样理解,把 src 的值赋值给变量 VPATH,所以在执行 make 的时候会从 src 目录下找我们需要的文件。
VPATH := src
当存在多个路径的时候我们可以这样写:
VPATH := src car
或着
VPATH := src:car
多个路径之间要使用空格或者是冒号隔开,表示在多个路径下搜索文件。搜索的顺序为我们书写时的顺序。先搜索 src 目录下的文件,再搜索 car 目录下的文件。
注意:无论你定义了多少路径,make 执行的时候会先搜索当前路径下的文件,当前目录下没有我们要找的文件,才去 VPATH 的路径中去寻找。如果当前目录下有我们要使用的文件,那么 make 就会使用我们当前目录下的文件。
实例:
VPATH=src car
test:test.o
gcc -o $@ $
假设 test.c 文件没有在当前的目录而在当前文件的子目录 "src" 或者是 "car" 下,程序执行是没有问题的,但是生成的 test 的文件没有在定义的子目录文件中而是在当前的目录下,当然生成文件路径可以指定。
2)、vpath的使用
再来了解一下关键字搜索 vpath 的使用,这种搜索方式一般被称作选择性搜索。使用上的区别我们可以这样理解:VPATH 是搜索路径下所有的文件,而 vpath 更像是添加了限制条件,会过滤出一部分再去寻找。
具体用法:
1) vpath PATTERN DIRECTORIES 2) vpath PATTERN 3) vpath
( PATTERN:可以理解为要寻找的条件,DIRECTORIES:寻找的路径 )
用法一,命令格式如下:
vpath test.c src
可以这样理解,在 src 路径下搜索文件 test.c。多路径的书写规则如下:
vpath test.c src car 或者 vpath test.c src : car
用法二,命令格式如下:意思是清除符合文件 test.c 的搜索目录。
vpath test.c
用法三,命令格式如下:vpath 单独使的意思是清除所有已被设置的文件搜索路径。
vpath
另外在使用 vpath 的时候,搜索的条件中可以包含模式字符“%”,这个符号的作用是匹配一个或者是多个字符,例如“%.c”表示搜索路径下所有的 .c 结尾的文件。如果搜索条件中没有包含“%" ,那么搜索的文件就是具体的文件名称。
Makefile隐含规则
所谓的隐含规则就是需要我们做出具体的操作,系统自动完成。编写 Makefile 的时候,可以使用隐含规则来简化Makefile 文件编写。
实例:
test:test.o gcc -o test test.o test.o:test.c
我们可以在 Makefile 中这样写来编译 test.c 源文件,相比较之前少写了重建 test.o 的命令。但是执行 make,发现依然重建了 test 和 test.o 文件,运行结果却没有改变。这其实就是隐含规则的作用。在某些时候其实不需要给出重建目标文件的命令,有的甚至可以不需要给出规则。实例:
test:test.o
gcc -o test test.o
注意:隐含条件只能省略中间目标文件重建的命令和规则,但是最终目标的命令和规则不能省略。
隐含规则的具体的工作流程:make 执行过程中找到的隐含规则,提供了此目标的基本依赖关系。确定目标的依赖文件和重建目标需要使用的命令行。隐含规则所提供的依赖文件只是一个基本的(在C语言中,通常他们之间的对应关系是:test.o 对应的是 test.c 文件)。当需要增加这个文件的依赖文件的时候要在 Makefile 中使用没有命令行的规则给出。实例:
test:test.o gcc -o test test.o test:test1.h
有些时候隐含规则的使用会出现问题。因为有一个 make 的“隐含规则库”。库中的每一条隐含规则都有相应的优先级顺序,优先级也就会越高,使用时也就会被优先使用。例如在 Makefile 中添加这行代码:
foo.o:foo.p
.p 文件是 Pascal 程序的源文件,如果书写规则时不加入命令的话,那么 make 会按照隐含的规则来重建目标文件 foo.o。如果当前目录下恰好存在 foo.c 文件的时候,隐含规则会把 foo.c 当做是 foo.o 的依赖文件进行目标文件的重建。因为编译 .c 文件的隐含规则在编译 .p 文件之前,显然优先级也会越高。当 make 找到生成 foo.o 的文件之后,就不会再去寻找下一条规则。如果我们不想使用隐含规则,在使用的时候不仅要声明规则,也要添加上执行的命令。
如果不明确的写下规则,那么make 就会自己寻找所需要的规则和命令。当然我们也可以使用 make 选项:-r
或-n-builtin-rules
选项来取消所有的预设值的隐含规则。当然即使是指定了“-r”的参数,某些隐含规则还是会生效。因为有很多的隐含规则都是使用了后缀名的规则来定义的,所以只要隐含规则中含有“后缀列表”那么隐含规则就会生效。默认的列表是
内嵌隐含规则的命令中,所使用的变量都是预定义的。我们将这些变量称为“隐含变量”。这些变量允许修改:可以通过命令行参数传递或者是设置系统环境变量的方式都可以对它进行重新定义。无论使用哪种方式,只要 make 在运行的,这些变量的定义有效。Makefile 的隐含规则都会使用到这些变量
比如我们编译 .c 文件在我们的 Makefile 中就是隐含的规则,默认使用到的编译命令时cc
,执行的命令时cc -c
我们可以对用上面的任何一种方式将CC
定义为ncc
。这样我们就编译 .c 文件的时候就可以用ncc
进行编译。
隐含规则中使用的变量可以分成两类:
1.代表一个程序的名字。例如:“CC”代表了编译器的这个可执行程序。
2.代表执行这个程序使用的参数.例如:变量“CFLAGS”。多个参数之间使用空格隔开。
下面我们来列举一下代表命令的变量,默认都是小写。
- AR:函数库打包程序,科创价静态库 .a 文档。
- AS:应用于汇编程序。
- CC:C 编译程序。
- CXX:C++编译程序。
- CO:从 RCS 中提取文件的程序。
- CPP:C程序的预处理器。
- FC:编译器和与处理函数 Fortran 源文件的编译器。
- GET:从CSSC 中提取文件程序。
- LEX:将Lex语言转变为 C 或 Ratfo 的程序。
- PC:Pascal 语言编译器。
- YACC:Yacc 文法分析器(针对于C语言)
- YACCR:Yacc 文法分析器