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 就会根据文件的扩展名找到相应的隐含规则,然后按照隐含规则来更新目标。隐式规则的例子是:
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 文件中的目标分为两类:实目标和伪目标。
实目标是真正要生成的以文件形式存放在磁盘上的目标。如:
hello:hello.o
$(CC) $^ -o $@ # 链接
其中, hello
文件就是实目标;
而伪目标不要求生成实际的文件,它主要是用于完成一些辅助操作。如:
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
这种伪目标,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行。
以上就是本次的分享,欢迎转发!
参考资料:
《嵌入式Linux上的C语言编程实践》、
https://segmentfault.com/a/1190000003756084#articleHeader4、
https://blog.csdn.net/haoel/article/details/2886