规则(1)
转自 http://blog.csdn.net/todd911/article/details/42750233
1.通配符
当你有一长串文件要制定时,为了简化此过程,make提供了通配符(wildcard),此功能也被称为文件名模式匹配。make
的通配符如同Bourne shell的~、*、?、[...]和[^...]。举例来说,*.*会被扩展成文件名中包含点号的所有文件,一个问号代表
任何单一字符,而[...]代表一个字符集。若要取得字符集的补集,则可以使用[^...]。
此外,“~”符号可以用来代表当前用户的主目录,一个“~”符号之后若跟着用户的名称则代表该用户的主目录。
2.假想工作目标
以工作目标充当标签来代表命令脚本,通常会有些用处。任何不代表文件的工作目标就叫做假想工作目标(phony target)。
另一个标准的假想工作目标称为clean:
clean:
rm -f *.o
通常,make总是会执行假想工作目标,因为对应于该规则的命令并不会创建以该工作目标为名称的文件。
切记,make无法区分文件形式的工作目标和假想工作目标,如果当前目录中刚好出现于假想工作目标同名的文件,make
将会在它的相依图中建立该文件与假想工作目标的关系。例如,运行make clean时,工作目录中刚好存在clean这个文件,
那么将会产生令人困惑的信息:
make clean
make:‘clean’ is up to date.
因为大多数的假想工作目标并未指定必要条件,clean工作目标总是被视为已经更新,所有相应的命令不会被执行。
为了避免这个问题,GNU make提供了一个特殊的工作目标-----.PHONY,用来告诉make,该工作目标不是一个真正的
文件。当你要声明假想工作目标时,只要将该工作目标指定成.PHONY的一个必要条件即可:
.PHONY:clean
clean:
rm -f *.o
现在,即使当前目录中存在名为clean的文件,make还是会执行对应于clean的命令。
许多makefile多少都会包含一组标准的假想工作目标,下面列出了这些标准的假想工作目标。
工作目标 功能
all 执行编译应用程序的所有工作。
install 从已编译的二进制文件进行应用程序的安装
clean 将产生自源代码的二进制文件删除
distclean 删除编译过程中所产生的任何文件。
TAGS 建立可供编辑其使用的标记表。
info 从Texinfo源代码来创建GNU info文件。
check 执行与应用程序相关的任何测试。
3.变量
最简答的变量具有如下的语法:
${variale-name}
这代表我们想要扩展名为variable-name的变量。任何文字都可以包含在变量之中,而且大多数字符都可以用在变量名称
上。一般来说,必须$()或${}将变量名称括住,这样make才会认得。
通常makefile文件都会定义许多变量,不过其中有许多特殊变量是make自动定义的。这些变量中若干可控制make
的行为,其余变量则是供make用来跟用户的makefile文件沟通。
4.自动变量
当规则相符时,make会设定自动变量。通过它们,你可以取用工作目标以及必要条件中的元素,所以你不必指明任何文件
名称。要避免重复,自动变量就相当有用,它们也是定义较一般模式规则中不可少的项目。
下面是7个核心的自动变量:
$@ 工作目标的文件名。 //针对每一个目标都适用
$% 档案文件成员结构中的文件名元素。
$< 第一个必要条件的文件名。
$? 时间戳在工作目标(时间戳)之后的所有必要条件,并以空格隔开这些必要条件。
$^ 所有必要条件的文件名,并以空格隔开这些文件名。这份列表已经删除重复的文件名,因为对大多数的应用而言,比如
编译、复制等,并不会用到重复的文件名。
$+ 如同$^,代表所有必要条件的文件名,并以空格隔开这些文件,它包含重复的文件名。此变量会在特殊的状况下被创建,
比如将自动变量传递给连接器时重复的值是有意义的。
$* 工作目标的主文件名。一个文件名称是由两个部分组成:主文件名和扩展名。请不要在模式规则以外使用此变量。
之前的makefile文件:
main:main.o myutil.o
gcc main.o myutil.o -o main
main.o:main.c myutil.h
gcc -c main.c myutil.h
myutil.o:myutil.c myutil.h
gcc -c myutil.c
替换成适当的自动变量后:
main:main.o myutil.o
gcc $^ -o $@
main.o:main.c myutil.h
gcc -c $^
myutil.o:myutil.c myutil.h
gcc -c $^
5.以VPAH和vpath来查找文件
到目前为止的例子都很简单:makefile与所有的源文件都存放在同一目录下。真实的项目中可能会比较复杂。按源代码
树的布局惯例,头文件会被放到include目录中,而源文件会被放到src目录里,并把makefile放到它们的上层目录,布局
如下:
根目录
|---makefile
|----|include|
| |___myutil.h
|___|src|
|----myutil.c
|__main.c
各文件内容如下:
main.c:
#include <stdio.h>
#include "myutil.h"
int main(void) {
myprint();
return 0;
}
myutil.h:
void myprint();
myutil.c:
#include <stdio.h>
void myprint(void) {
printf("this is myprint function.\n");
}
makefile:
main:main.o myutil.o
gcc $^ -o $@
main.o:main.c myutil.h
gcc -c $^
myutil.o:myutil.c myutil.h
gcc -c $^
执行make的结果如下:
make: *** No rule to make target `main.c', needed by `main.o'. Stop.
因为main.c不在当前目录,所以make找不到这个源文件,我们可以使用VPATH和vpath来告诉make到不同的目录去查找
源文件,可以再makefile文件中对VPATH进行如下赋值动作:
VPATH=src include
执行make,依然不行:
gcc -c src/main.c include/myutil.h
src/main.c:2:20: fatal error: myutil.h: No such file or directory
compilation terminated.
make: *** [main.o] Error 1
因为gcc无法找到引入文件,我们只要使用正确的-I选项来自定义隐含编译规则就可以解决这个问题了:
VPATH=src include
CPPFLAGS=-I include
main:main.o myutil.o
gcc $(CPPFLAGS) $^ -o $@
main.o:main.c myutil.h
gcc $(CPPFLAGS) -c $^
myutil.o:myutil.c myutil.h
gcc $(CPPFLAGS) -c $^
执行make:
gcc -I include -c src/main.c include/myutil.h
gcc -I include -c src/myutil.c
gcc -I include main.o myutil.o -o main
成功编译。
VPATH变量的内容是一份目录列表,可供make搜索其所需要的文件。这份目录列表可用来搜索工作目标以及必要条件,
但不包含脚本中提及的文件。这份目录列表的分割符在Unix上可以是空格或冒号,在windows上可以使空格或分号。
虽然VPATH变脸可以解决以上的搜索问题,但是也有限制。make将会为它所需要的任何文件搜索VPATH列表中的每个目录,
如果多个目录出现同名的文件,则make只会获取第一个被找到的文件,这可能会造成问题。
此时可以使用vpath指令,这个指令的语法如下:
vpath pattern directory-list
所有之前的VPATH变量可以改写成:
vpath %.c src
vpath %.h include
现在我们告诉了make应该在src目录中搜索.c文件,在include目录中搜索.h文件。