Makefile总述(三)

Makefile里有什么

Makefile中主要包含五个东西:显示规则,隐晦规则,变量定义,文件指示和注释
显示规则:说明了如何生成一个或多个目标文件,这是由Makefile的书写着明确指示的要生成的文件,需要依赖的文件,生成命令
隐晦规则:由make自动推导的功能,可以让我们写一些比较粗糙的Makefile文件
变量定义:在Makefile中我们需要定义一系列的变量,变量一般都是字符串,相当于C中的宏,当Makefile被执行的时候,其中的变量都会被扩展到相应的引用位置上
文件指示:包括三个部分,一个是Makefile中引用另一个Makefile,就像C中的include一样;另一个是指根据某些情况,制定Makefile的有效部分,就像C语言中的预编译#if一样;还有一个就是定义多行命令
注释:Makefile只有行注释,跟shell脚本一样,使用关键字符#,如果我们需要使用#字符,通过反斜杠()进行转义,如\#

Makefile的文件名

在默认情况下,make命令会在当前目录下顺序查找GNUmakefilemakefileMakefile的文件,找到了解析这些文件。当然我们可以使用别的文件名来书写Makefile,例如Make.Linux,Make.Solaris,Make.AIX等,如果要制定特定的Makefile,我们可以使用make的-f或者-file参数,例如make -f Make.Linux或者make -file Make AIX

引用其他的Makefile

在Makefile使用include关键字可以吧别的Makefile包含进来,有点像C语言中的#include,被包含的文件会原模原样的放在当前文件包含位置,include语法是:

include <filename>

filename可以是当前操作系统Shell的文件模式,可以包含路径和通配符
在include前面有一些空字符,但却不是tab键开始。include可以用一个或者多个空格隔开。假如我们有这样几个Makefile文件:a.mkb.mkc.mk,还有一个文件叫做foo.make,以及一个变量$(bar),其中包含了e.mkf.mk,那么下面的语句“

bar = e.mk f.mk
include foo.make *.mk $(bar)

等价于

include foo.make a.mk b.mk c.mk e.mk f.mk

在我们使用include的时候,如果有文件没有找到,make会产生一条警告,但是不会立刻有执行错误,他会继续载入其他的文件,一旦完成makefile的提取,他会重新找那些没有找到的或者没有读取成功的文件,如果还是不行,则会产生一个致命错误,如果我们想要让make不理那些无法读取的文件。而继续执行,我们可以在include前加上-,例如

-include <filename>

表示无论include过程中遇到什么错误,都不要报错,继续执行

环境变量MAKEFILES

如果当前环境中定义了环境变量MAKEFILES,那么make会把这个变量中的值做一个类似于include的动作,这个变量中的值是其他的Makefile,用空格隔开。指示他和include不同的是,从这个环境中引入Makefile的目标不会一起作用。

make的工作方式

GNU的make工作时的执行步骤是:

  1. 读取所有的Makefile
  2. 读取被include的其他Makefile
  3. 初始化文件中的变量
  4. 推导隐晦规则,并且分析所有规则
  5. 为所有的目标文件创建依赖关系链
  6. 根据依赖关系,决定哪些目标要重新生成
  7. 执行生成命令

Makefile书写规则

规则包含两个部分,一个是依赖关系,一个是生成目标的方法
在Makefile中,规则的顺序很重要,因为Makefile中只应该有一个最终目标,其他的目标都是被这个目标所连带出阿里的,所以要让make知道我们的最终目标是什么。一般情况下,定义在Makefile中的目标可能会有很多,但是第一条规则中的目标将被确立为最终目标。如果第一条规则中的目标有很多,那么第一个目标位终极目标,make所完成的也就是这个目标

规则举例

#foo模块
foo.o:foo.c defs.h 
        gcc -c -g foo.c

从这个例子中我们知道foo.o是我们的目标,foo.cdefs.h是目标所依赖的源文件,而只有一个命令gcc -c -g foo.c
这个规则告诉我们两件事情:

  1. 文件的依赖关系,foo.o依赖于foo.cdefs.h文件,如果foo.cdefs.h的文件日期要比foo.o文件日期新,或者foo.o不存在,那么依赖关系发生
  2. 生成foo.o文件

在规则中使用通配符

如果我们想定义一系列比较类似的文件,我们很自然的就想到使用通配符,make支持三种通配符,*?[...]
~ 在Linux中表示用户根目录。例如~/test就表示当前用户的$HOME目录下的test目录,而如果是~paul/test表示的用户paul的宿主目录下的test目录,而在windows下,用户没有宿主目录,那么~代表的是环境变量中HOME的位置
* 想要去通过标识符去识别一系列文件的总称,例如*.c表示所有后缀为.c的文件。同样这里是可以使用转义符的。

Makefile中的文件搜索

在一些大工程中,我们有大量的源文件,通常需要把这些源文件进行分类,在java中这叫分包,那么也就是放在不同的目录下。所以当make需要去寻找文件的依赖关系时,我们可以在文件前加上路径,最好的方式还是把一个路径告诉make,让make自己去找
Makefile中的特殊变量VPATH就是完成这个功能的,如果没有指明这个变量,make只会在当前的目录中去寻找依赖,如果定义了这个变量,make就会在当前目录下去找依赖,如果找不到,再去指定的目录下去找。

VPATH = src:../headers

上面的定义制定了两个目录,src../headers,make会按照这个顺序进行搜索,目录有冒号:分割
另一个设置文件搜索路径的方法是使用make的vpath关键字(小写),这里不是变量,与上面的VPATH类似,但是它更加灵活,他可以指定不同的文件在不同的搜索目录中,三种使用方式

  1. vpath 为符合模式的文件制定搜索目录
  2. vpath 清除符合模式的文件搜索目录
  3. vpath 清除所有已被设置好的文件搜索目录

vpath使用方法中的需要包含%字符,%字符的意思是匹配零或者若干字符,例如%.h表示所有已.h结尾的文件

vpath %.h ../headers

该语句表示要求make在../headers目录下搜索所有以.h结尾的文件
同时我们可以连续的使用vpath语句,以指定不同搜索策略,如果连续的语句中出现了相同的<pattern>或者被重复了的<pattern>,则make会按照先后顺序去执行搜索

vpath %.c foo
vpath % blish
vpath %.c bar

表示.c结尾的文件,现在foo目录,然后是blish,最后是bar目录

vpath %.c foo:bar
vpath % blish

伪目标

有一个例子,如下

#写在Makefile中的
objects = main.o kdb.o command.o
edit:main.o kdb.o command.o
        gcc -o edit $(objects)
main.o:defs.h buffers.h
kdb.o:defs.h
command.o: buffers.h
clean:
        rm *.o edit

我们在执行make的时候生成了很多编译文件,我们也应该提供一个清除他们的target–clean。虽然我们的clean是一个target,但是我们并不需要生成文件,所以我们可以使用伪目标的方式。
伪目标不是一个文件,只是一个标签,由于伪目标不是文件,所以make无法生成它的依赖关系和决定是否要执行他。我们只有通过显示的指明这个target才会生效,注意伪目标不能和文件名重名。
为了避免和文件重名,我们可以使用特殊标记.PHONY来显示的指明这个目标是一个伪目标,向make指明,不管是否有这个文件,这个目标就是伪目标

.PHONY:clean

只要有这个声明,不管是否有clean文件,要运行clean这个目标,只有make clean这样,完整的写法是

.PHONY:clean
clean:
        rm *.o edit

伪目标一般没有依赖的文件,但是我们也可以为伪目标指定所依赖的文件。伪目标同样可以作为默认目标,只要将其放在第一个。

all : prog1 prog2 prog3
.PHONY:all
prog1:prog1.o utils.o
        gcc -o prog1 $@
prog2:prog2.o buffers.o
        gcc -o prog2 $@
prog3:prog3.o kdb.o
        gcc -o prog3 
grog1.o:grog1.c defs.h buffers.h
        gcc -c grog1.c
grog2.o:grog2.c defs.h
        gcc -c grop2.c
grog3.o:grog3.c buffers.h
        gcc -c grop3.c

多目标

Makefile的规则中的目标可以不止一个,其支持多个目标,有可能我们的多个目标同时依赖一个文件,并且其声称的命令大体类似,。当然多个目标的生成规则的执行命令是同一个,这里我们可能有问题,但是我们可以使用一个自动化变量$@,这个变量表示目前规则中所有目标的集合。例子看这里

静态模式

静态模式可以更加容易的定义多目标的规则,可以让我们的规则变得更加有弹性和灵活,语法如下

<targets...>:<target-pattern>:<prereq-patterns..>
        <commands>

targets定义了一系列的目标文件,可以有通配符,是一个目标的集合
target-parrtern指明了targets的模式,也就是的目标集模式
prereq-parrterns是目标的依赖模式,他对target-parrtern形成的模式在进行一次依赖目标的定义

参考资料

教你写Makefile

猜你喜欢

转载自blog.csdn.net/weixin_42580207/article/details/81244273
今日推荐