makefile规则

    书写规则时我们需要注意的几点:
1. 规则的命令部分有两种书写方式:a. 命令可以和目标:依赖描述放在同一行。命令在依赖文件列表后并使用分号(;)和依赖文件列表分开。b. 命令在目标:依赖的描述的下一行,作为独立的命令行。当作为独立的命令行时此行必须以[Tab]字符开始。在Makefile中,在第一个规则之后出现的所有以[Tab]字符开始的行都会被当作命令来处理。
2. Makefile中符号“$”有特殊的含义(表示变量或者函数的引用),在规则中需要使用符号“$”的地方,需要书写两个连续的(“$$”)。
3. 前边已提到过,对于Makefile中一个较长的行,我们可以使用反斜线“\”将其书写到几个独立的物理行上。虽然make对Makefile文本行的最大长度是没有限制的,但还是建议这样做。不仅书写方便而且更有利于别人的阅读(这也是一个程序员修养的体现)。

“order-only”依赖的使用举例:
LIBS = libtest.a
foo : foo.c | $(LIBS)
$(CC) $(CFLAGS) $< -o $@ $(LIBS)
make在执行这个规则时,如果目标文件“foo”已经存在。当“foo.c”被修改以后,目标“foo”将会被重建,但是当“libtest.a”被修改以后。将不执行规则的命令来重建目标“foo”。
就是说,规则中依赖文件$(LIBS)只有在目标文件不存在的情况下,才会参与规则的执行。当目标文件存在时此依赖不会参与规则的执行过程。

(1)文件名使用通配符

        在Linux(unix)中,以波浪线“~”开始的文件名有特殊含义。单独使用它或者其后跟一个斜线(~/),代表了当前用户的宿主目录(在shell下可以通过命令“echo ~(~\)”来查看)。例如“~/bin”代表“/home/username/bin/”(当前用户宿主目录下的bin目录)。波浪线之后跟一个单词(~word),代表由这个“word”所指定的用户的宿主目录。例如“~john/bin”就是代表用户john的宿主目录下的bin目录。

自动环变量“$?”在这里表示依赖文件列表中被改变过的所有文件。变量定义中不能使用通配符。

(2)函数wildcard和patsubst

       之前提到过,在规则中,通配符会被自动展开。但在变量的定义和函数引用时,通配符将失效。这种情况下如果需要通配符有效,就需要使用函数“wildcard”,它的用法是:$(wildcard PATTERN...) 。在Makefile中,它被展开为已经存在的、使用空格分开的、匹配此模式的所有文件列表。如果不存在任何符合此模式的文件,函数会忽略模式字符并返回空。

        一般我们可以使用“$(wildcard *.c)”来获取工作目录下的所有的.c文件列表。复杂一些用法;可以使用“$(patsubst %.c,%.o,$(wildcard *.c))”,首先使用“wildcard”函数获取工作目录下的.c文件列表;之后将列表中所有文件名的后缀.c替换为.o。这样我们就可以得到在当前目录可生成的.o文件列表。因此在一个目录下可以使用如下内容的Makefile来将工作目录下的所有的.c文件进行编译并最后连接成为一个可执行文件:
#sample Makefile
objects := $(patsubst %.c,%.o,$(wildcard *.c))
foo : $(objects)
cc -o foo $(objects)
这里我们使用了make的隐含规则来编译.c的源文件。

(3) 目录搜索

VPATH和vpath,两者的不同在于前者是一个特殊的环境变量,指定搜索目录时用VPATH=路径名。后者是一个关键字,用来为特定的文件指定搜索目录。

1、vpath PATTERN DIRECTORIES
为所有符合模式“PATTERN”的文件指定搜索目录“DIRECTORIES”。多个目录使用空格或者冒号(:)分开。类似上一小节的“VPATH”变量。
2、vpath PATTERN
清除之前为符合模式“PATTERN”的文件设置的搜索路径。
3、vpath
清除所有已被设置的文件搜索路径。
vapth使用方法中的“PATTERN”需要包含模式字符“%”。“%”意思是匹配一个或者多个字符,例如,“%.h”表示所有以“.h”结尾的文件。

(4)   命令行和搜索目录
       make在执行时,通过目录搜索得到的目标的依赖文件可能会在其它目录(此时依赖文件为文件的完整路径名),但是已经存在的规则命令却不能发生变化。因此,书写命令时我们必须保证当依赖文件在其它目录下被发现时规则的命令能够正确执行。
解决这个问题的方式是在规则的命令行中使用“自动化变量”诸如“$^”等。规则命令行中的自动化变量“$^”代表所有通过目录搜索得到的依赖文件的完整路径名(目录 + 一般文件名)列表。“$@”代表规则的目标。所以对于一个规则我们可以进行如下的描述:
foo.o : foo.c
cc -c $(CFLAGS) $^ -o $@
变量“CFLAGS”是编译.c文件时gcc的编译选项,可以在Makefile中给它指定明确的值、也可以使用隐含的定义值。

       规则的依赖文件列表中可以包含头文件,而在命令行中不需要使用这些头文件(这些头文件的作用只有在make程序决定目标是否需要重建时才有意义)。我们可以使用另外一个变量来书代替“$^”,如下:
VPATH = src:../headers
foo.o : foo.c defs.h hack.h
cc -c $(CFLAGS) $< -o $@
自动化变量“$<”代表规则中通过目录搜索得到的依赖文件列表的第一个依赖文件。

(5) 库文件和搜索目录
       Makefile中程序链接的静态库、共享库同样也可以通过搜索目录得到。这一特性需要我们在书写规则的依赖时指定一个类似“-lNAME”的依赖文件名(一个奇怪的依赖文件!一般依赖文件名应该是一个普通文件的名字。库文件的命名也应该是“libNAME.a”而不是所写的“-lNAME”。这是为什么,熟悉GNU ld的话我想这就不难理解了,“-lNAME”的表示方式和ld的对库的引用方式完全一样,只是我们在书写Makefile的规则时使用了这种书写方式。所以你不应该感到奇怪)。下边我们来看看这种奇怪的依赖文件到底是什么。

       当规则中依赖文件列表中存在一个“-lNAME”形式的文件时。make将根据“NAME”首先搜索当前系统可提供的共享库,如果当前系统不能提供这个共享库,则搜索它的静态库(当然你可以在命令行中使用连接选项来指定程序采用动态连接还是静态连接,这里我们不讨论)。来看一下详细的过程。1. make在执行规则时会在当前目录下搜索一个名字为“libNAME.so”的文件;2. 如果当前工作目录下不存在这样一个文件,则make会继续搜索使用“VPATH”或者“vpath”指定的搜索目录。3. 还是不存在,make将搜索系统库文件存在的默认目录,顺序是:“/lib”、“/usr/lib”和“PREFIX/lib”(在Linux系统中为“/usr/local/lib”,其他的系统可能不同)。
如果“libNAME.so”通过以上的途径最后还是没有找到的话,那么make将会按照以上的搜索顺序查找名字为“libNAME.a”的文件。

默认情况时,“.LIBPATTERNS”的值为:“lib%.so lib%.a”。这也是默认情况下在规则存在“-lNAME”格式的依赖时,链接生成目标时使用“libNAME.so”和“libNAME.a”的原因。

在Makefile中,一个伪目标可以有自己的依赖(可以是一个或者多个文件、一个或者多个伪目标)。在一个目录下如果需要创建多个可执行程序,我们可以将所有程序的重建规则在一个Makefile中描述。因为Makefile中第一个目标是“终极目标”,约定的做法是使用一个称为“all”的伪目标来作为终极目标,它的依赖文件就是那些需要创建的程序。下边就是一个例子:
#sample Makefile
all : prog1 prog2 prog3
.PHONY : all
prog1 : prog1.o utils.o
cc -o prog1 prog1.o utils.o
prog2 : prog2.o
cc -o prog2 prog2.o
prog3 : prog3.o sort.o utils.o
cc -o prog3 prog3.o sort.o utils.o
       执行make时,目标“all”被作为终极目标。为了完成对它的更新,make会创建(不存在)或者重建(已存在)目标“all”的所有依赖文件(prog1、prog2和prog3)。当需要单独更新某一个程序时,我们可以通过make的命令行选项来明确指定需要重建的程序。(例如:“make prog1”)。

       另外make存在一个内嵌隐含变量“RM”,它被定义为:“RM = rm –f”。因此在书写“clean”规则的命令行时可以使用变量“$(RM)”来代替“rm”,这样可以免出现一些不必要的麻烦!这是我们推荐的用法。

(6)关于makefile的特殊目标,在用到时现查现用。

(7) 静态模式

       静态模式规则是这样一个规则:规则存在多个目标,并且不同的目标可以根据目标文件的名字来自动构造出依赖文件。

首先,我们来看一下静态模式规则的基本语法:
TARGETS ...: TARGET-PATTERN: PREREQ-PATTERNS ...
COMMANDS
...
“TAGETS”列出了此规则的一系列目标文件。像普通规则的目标一样可以包含通配符。“TAGET-PATTERN”和“PREREQ-PATTERNS”说明了如何为每一个目标文件生成依赖文件。从目标模式(TAGET-PATTERN)的目标名字中抽取一部分字符串(称为“茎”)。使用“茎”替代依赖模式(PREREQ-PATTERNS)中的相应部分来产生对应目标的依赖文件。

(8) 双冒号规则

        双冒号规则就是使用“::”代替普通规则的“:”得到的规则。当同一个文件作为多个规则的目标时,双冒号规则的处理和普通规则的处理过程完全不同(双冒号规则允许在多个规则中为同一个目标指定不同的重建目标的命令)。

1. 双冒号规则中,对于一个没有依赖而只有命令行的双冒号规则,当引用此目标时,规则的命令将会被无条件执行。而普通规则,当规则的目标文件存在时,此规则的命令永远不会被执行(目标文件永远是最新的)。
2. 当同一个文件作为多个双冒号规则的目标时。这些不同的规则会被独立的处理,而不是像普通规则那样合并所有的依赖到一个目标文件。这就意味着对这些规则的处理就像多个不同的普通规则一样。就是说多个双冒号规则中的每一个的依赖文件被改变之后,make只执行此规则定义的命令,而其它的以这个文件作为目标的双冒号规则将不会被执行。

  • 0
    点赞
  • 0
    收藏
    觉得还不错? 一键收藏
  • 0
    评论

“相关推荐”对你有帮助么?

  • 非常没帮助
  • 没帮助
  • 一般
  • 有帮助
  • 非常有帮助
提交
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值