【Makefile】03-Makefile总述
1 Makefile里有什么
Makefile里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示、注释。
1.1 显式规则
显式规则用于说明如何生成一个或多个目标文件。Makefile的书写者要明确指出,要生成什么文件,它的依赖文件是什么,以及具体的生成命令。
1.2 隐晦规则
make有自动推导功能,利用隐晦规则,可以让我们较为简略地书写Makefile。
1.3 变量定义
在Makefile中,往往需要定义变量。变量通常是字符串,用法类似C语言中的宏,执行Makefile时,将变量扩展到相应的位置上。
1.4 文件指示
文件指示包括三个部分:
- 在一个Makefile中引用另一个Makefile,类似C语言中的include;
- 根据某些情况指定Makefile中的有效部分,类似C语言中的#if;
- 定义多行命令。
1.5 注释
Makefile中只有行注释,以#开始。如果要在Makefile中使用“#”字符,可以用反斜杠进行转义,如“#”。
需要强调的是,Makefile中的命令,必须以Tab键起始。
2 Makefile的文件名
2.1 默认情况
默认情况下,make命令会在当前目录下按顺序查找文件名为“GNUmakefile”、“makefile”、“Makefile”的文件。
在实际工程中,最好使用Makefile这个文件名。原因如下:
- Makefile的第一个字符为大写,较为醒目。
- GNUmakefile是GNU的make识别的,最好不要用。
2.2 使用自定义文件名
我们也可以自定义Makefile文件名,譬如“albertmake”等。可以使用make的-f和–file参数来指定特定的Makefile,指令如下:
make -f albertmake
make clean -f albertmake
3 引用其他的Makefile
3.1 引用其他Makefile的语法
在Makefile中,可以使用include关键字将其他Makefile包含进来。被包含的Makefile会原封不动地拷贝到当前文件中,这和C语言中的#include类似。
include的语法如下:
include <filename>
filename可以是当前操作系统Shell的文件模式,即可以包含路径和通配符。
在include前面,可以有一些空字符,但绝不能以Tab键开始。include和可以用一个或多个空格隔开。譬如对于以下目录结构,有两种等价的Makefile写法:
.
|____f.mk
|____b.mk
|____c.mk
|____Makefile
|____foo.make
|____e.make
|____a.make
bar = e.make f.make
include foo.make *.mk $(bar)
include foo.make a.mk b.mk c.mk e.make f.make
3.2 查找引用的Makefile
make命令开始时,会先查找include指出的其他Makefile,将其内容贴到当前的位置。如果没有指定绝对路径或相对路径,make首先在当前目录下查找。如果当前目录下没找到,还会在以下几个目录中查找:
- 如果make执行时,有“-I”或“–include-dir”参数,make就会在指定目录下查找。
- 如果目录/include存在(通常是/usr/local/bin或/usr/include),make也会去查找。
如果有文件没有找到,make会生成警告信息,但不会马上出现致命错误。make会继续载入其他文件,等到makefile读取完成时,make会再次尝试处理没有找到或无法读取的文件。如果再次查找还没有找到,才会出现致命信息。如果想让make不理会无法读取的文件,可以在include前面添加“-”。其含义是,无论include过程中出现什么错误,都不要报错,继续向下执行。也可以使用sinclude,以便和其他版本make兼容。作用和添加“-”是一样的。
4 环境变量MAKEFILES
如果当前环境定义了环境变量MAKEFILES,make会把这个变量中的值做一个类似include的动作。这个变量中的值是其它Makefile,用空格分隔。环境变量MAKEFILES和include的区别是,通过MAKEFILES环境变量引入的Makefile中的target不会起作用。如果环境变量中定义的文件发现错误,make也不会理会。
一旦定义了MAKEFILES环境变量,所有Makefile都会受影响。我们应当尽量避免使用MAKEFILES。如果Makefile出了怪问题,也要排查一下是否定义了MAKEFILES。
5 make的执行步骤
GNU make的执行步骤如下,其他make也大同小异:
- 第一阶段
- 读入所有的Makefile
- 读入被include的Makefile
- 初始化Makefile中的变量
- 推导隐晦规则,并分析所有规则
- 为所有目标文件创建依赖关系链
- 第二阶段
- 根据依赖关系,决定要重新生成哪些目标文件
- 执行生成命令
第一阶段中,如果定义的变量被使用了,make会将其展开在使用的位置。但如果变量出现在依赖关系的规则中,make不会马上展开,仅当这条依赖要被使用时,才会展开变量。