在Linux上进行C/C++开发时我们通常使用GNU make工具来编译整个工程。接触过ARM Linux的朋友相信对于make和Makefile并不陌生。make只是一个工具,它在工作的时候需要一个名为Makefile的文件来告诉它如何去工作。
Makefile的核心规则非常简单,它描述了目标文件及其依赖的文件以及当依赖文件更新时应该被执行的命令:
目标: [依赖项1] [依赖项2] ...
[<键盘tab键>命令]
或:
目标: [依赖项1] [依赖项2] ...;[<键盘tab键>命令]
注意:命令必须以tab键开头([]代表可选项)
以编译hello.c的Makefile为例:
当执行make命令时,make会在当前目录下寻找Makefile文件。如果没有为make指定目标,一般将Makefile中的第一个目标作为最终目标。在上面这个Makefile中,第一个目标就是hello。hello拥有一个依赖文件(hello.o),当hello.o的修改日期比hello新时,会执行“gcc -o hello hello.o”命令来生成新的可执行文件hello。同样的,第5行的目标是hello.o,它依赖于hello.c这个文件。第5、6行指明了hello.o的依赖以及应该如何编译。整个流程是:如果我们修改了hello.c,hello.c的日期就会比hello.o新,此时make就会执行“gcc -c hello.c”来更新hello.o。这时候又因为hello.o比hello的日期新,因此make又会去执行第三行来更新hello
![](https://i-blog.csdnimg.cn/blog_migrate/df1b130a62f5badecc5e27c2be355e4a.png)
.PHONY表明clean是一个伪目标。clean并没有依赖的文件,当make执行它时它会将所有的.o文件以及编译生成的hello文件删除。整个工程中又只剩下.c文件及Makefile文件,非常清爽~~~~
![](https://i-blog.csdnimg.cn/blog_migrate/a6b11169280e3aa8343a6649caa4c21e.png)
通常而言一个工程中会存在很多.c文件,倘若像上面的Makefile中一样手动编写每一个.c文件的规则,这对于任何人来说都是一个噩梦!不过好在强大的make会自动推导命令和文件,这就是“隐含规则”
既然要使用隐含规则,那便不要再去编写具体的规则,让make自己去推导就好啦。在此之前,已经将printf("hello world!\r\n")放到了hello.c的hello()中,main.c通过调用hello()来输出“hello world!”。上文中我们编写了hello.o的规则,现在我们将其删掉,让make自己去推导:
![](https://i-blog.csdnimg.cn/blog_migrate/ab534e87cb73a5c1647e37da9a2614d9.png)
在新的Makefile中我们并没有定义.o文件的规则,但是在编译的过程中make确实为我们编译出了.o文件,同时最终生成的可执行文件main也确实打印出了“hello world!”:
![](https://i-blog.csdnimg.cn/blog_migrate/f3a232395532bf5c1e7cf68d588a6893.png)
从终端的输出中可以看到,make使用了隐含规则中的变量CC来调用默认的编译命令cc编译得到了main.o和hello.o,最后使用我们在Makefile中明确指定的规则来生成可执行文件main。那么问题来了,如果希望自己定义模式规则该怎么办呢??
我们可以通过编写模式规则来定义隐式规则:
Makefile的第7行指出.o文件依赖.c文件。例如,hello.o的依赖文件就是hello.c
自动变量表(部分) | |
---|---|
% | 表示一个或多个任意字符。例如,%.c中的%代表的就是该.c文件的文件名 |
$() | 引用变量。例如,引用变量CC -> $(CC) |
$@ | 规则目标的文件名。例如:main.c、hello.c或main |
$^ | 所有依赖目标的集合 |
$< | 第一个依赖的名称 |
进一步将自己的Makefile进行整理,为了便于区分,这里将上文make生成的可执行文件的名称由main改成了run:
![](https://i-blog.csdnimg.cn/blog_migrate/6af81f63b2312d799f68040fcc73f9a8.png)
编译执行: