《GNU make项目管理》笔记--规则(1)

来源:互联网 发布:阿里云 进销存 编辑:程序博客网 时间:2024/05/01 22:05

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文件。

0 0
原创粉丝点击