1、前言
上一篇笔记分享了使用make工具编译C程序的方法(【Linux笔记】make工程管理工具(一)),但是还未分享make工具是什么,本篇笔记就来看一下make工具是什么吧。
make和Makefile(或者makefile)文件提供了一种简单有效的工程管理方式。Makefile文件是一个决定着如何编译工程的文本,有一定的书写规则。make是一个GNU命令工具,用来解释Makefile文件中的命令来编译我们的工程。
2、make工具
make
是一个命令工具,是一个解释 Makefile
中指令的命令工具,一般来说,大多数的 IDE(集成开发环境) 都有这个命令,比如:Delphi
的 make,Visual C
的 nmake。但是在Linux
下一般不具有IDE,所以我们需要学会如何使用make工具来编译。
3、Makefile文件的书写规范
Makefile
文件中最重要的是规则。规则的格式如下:
target:prerequisites
command
其中,target
为目标,prerequisites
为依赖。command
为make需要执行的命令。
- 目标:往往是程序的中间或者最终生成的文件名,比如目标文件、可执行文件等。
- 依赖:是指用来产生目标文件的输入文件名,一个目标往往依赖于一个或多个文件。
- 命令:是指任何一个文件发生改动之后,需要重新生成目标文件需要执行的命令,这里可以有多条命令,但是每个命令必须单独占一行,且需要注意的是,每个命令的前面必须有一个
<tab键>
,因为make是用过来识别命令行的,进而完成相应的动作。
例如,上一篇我们的笔记中为编译hello.c
书写的Makefile
文件如下:
其中,第1、2
行为一条规则,第4、5
行为一条规则,第7、8
行为一条规则,第10、11
行为一条规则,第13、14
行为一条规则。
拿第1、2
行这一条规则来举例说明, hello
为目标文件, hello.o
为依赖文件, gcc hello.o -o hello
为命令。除此之外,这个文件中, #
符号后面是注释, clean
是一个特殊的目标,它没有依赖,称为伪目标
。
规则是Makefile文件中的最基本的、也是最核心的部分。Makefile中还有其他内容。
Makefile文件主要包含5个方面内容:显示规则、隐式规则、变量、文件指示、注释、实目标与伪目标等。
(1)显式规则
。显式规则说明了,如何生成一个或多的的目标文件。这是由 Makefile 的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。如
hello:hello.o
gcc hello.o -o hello
这就是一条显示规则。
(2)隐式规则
。隐含规则是系统或用户预先定义好的一些特殊规则,主要是一些常用的依赖关系和更新命令。一般规则使用文件的全名,而隐含规则中出现的目标文件和依赖文件都只使用文件的扩展名。
如果Makefile 文件里面没有显式给出文件的依赖关系的时候,make 就会根据文件的扩展名找到相应的隐含规则,然后按照隐含规则来更新目标。隐式规则的例子是:
代码语言:javascript复制hello:hello.o
$(CC) $^ -o $@
(3)变量定义
。在 Makefile 中我们要定义一系列的变量,变量一般都是字符串,这个有点像 C 语言中的宏,当 Makefile 被执行时,其中的变量都会被扩展到相应的引用位置上。如:
其中 CC
为变量的定义, $(CC)
为变量的调用。 $^
、 $@
也是变量的调用,这两个变量是Makefile文件中最常用的变量(不需要自己定义),$^
代表当前规则中所有的依赖文件, $@
代表当前规则的目标文件。
(4)文件指示
。其包括了三个部分,一个是在一个 Makefile 中引用另一个 Makefile,就像 C 语言中的 include
一样;另一个是指根据某些情况指定 Makefile 中的有效部分,就像 C 语言中的预编译#if
一样;还有就是定义一个多行的命令。
(5)注释
。Makefile 中只有行注释,和 UNIX 的 Shell
脚本一样,其注释是用 #
字符,这个就像C/C
中的//
一样。如果你要在你的Makefile 中使用#
字符,可以用反斜框进行转义,如:#
。
(6)实目标与伪目标
。Makefile 文件中的目标分为两类:实目标和伪目标。
实目标是真正要生成的以文件形式存放在磁盘上的目标。如:
代码语言:javascript复制hello:hello.o
$(CC) $^ -o $@ # 链接
其中,hello
文件就是实目标;
而伪目标不要求生成实际的文件,它主要是用于完成一些辅助操作。如:
代码语言:javascript复制clean:
rm -rf hello.i hello.s hello.o hello # 删除编译生成文件
其中的 clean
就是一个伪目标。我们在命令里面输入命令: make clean
就可以执行删除操作: rm -rf hello.i hello.s hello.o hello
。
但是这种书写形式不是很严谨,因为可能在当前目录下面存在文件名为 clean 的文件,因为这时候, 后面没有依赖文件,所以make 就认为这个文件是最新的,所以就不会执行 rm -rf hello.i hello.s hello.o hello
。所以为了避免这种情况的发生,所以建议使用这种写法:
.PHONY:clean
clean:
rm -rf hello.i hello.s hello.o hello # 删除编译生成文件
使用 .PHONY
说明clean是一个伪目标。
4、make是如何工作的?
当我们只输入make
命令。那么:
(1)make 会在当前目录下找名字叫Makefile
或makefile
的文件。
(2)如果找到,它会找文件中的第一个目标文件(target
),在上面的例子中,它会找到hello
这个文件,并把这个文件作为最终的目标文件 。
(3)如果 hello
文件不存在,或是hello
所依赖的后面的 .o
文件,的文件修改时间要比 hello
这个文件新,那么,他就会执行后面所定义的命令来生成 hello 这个文件。
(4)如果hello
所依赖的 .o
文件也存在,那么make
会在当前文件中找目标为.o
文件的依赖性,如果找到则再根据那一个规则生成.o
文件。(这有点像一个堆栈的过程)
(5)当然,我们的c
文件和h
文件是存在的啦,于是 make
会生成 .o
文件,然后再用 .o
文件生命make
的终极任务,也就是执行文件 hello
了。
这就是整个make
的依赖性,make
会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么 make 就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make 根本不理。
像clean
这种伪目标,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行。
以上就是本次的笔记分享,主要介绍make工具、Makefile文件大概有哪些内容、make是如何工作的。如有错误,欢迎指出!
参考资料:
代码语言:javascript复制《嵌入式Linux上的C语言编程实践》、
https://segmentfault.com/a/1190000003756084#articleHeader4、
https://blog.csdn.net/haoel/article/details/2886
如果你觉得对你有用的话,麻烦帮忙点个赞。如果你觉得对你的同学与朋友也有用的话,欢迎转发。