【Linux】make工程管理工具(二)


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 会在当前目录下找名字叫Makefilemakefile的文件。

(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


文章作者: 杂烩君
版权声明: 本博客所有文章除特別声明外,均采用 CC BY 4.0 许可协议。转载请注明来源 杂烩君 !
  目录