欢迎您访问程序员文章站本站旨在为大家提供分享程序员计算机编程知识!
您现在的位置是: 首页

Linux:详细解析项目自动化构建工具--make/Makefile

程序员文章站 2022-07-14 15:24:50
...


背景

  • 会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力

  • 一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作

  • makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。

  • make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。

  • make是一条命令,makefile是一个文件,两个搭配使用,完成项目自动化构建。

make/Makefile:项目自动化构建工具

Makefile: 记录项目构建规则流程的普通文件

make: Makefile解释器,当前命令行下执行make,这时候这个解释程序会到当前路径下寻找名字叫Makefile的文件,对其内部的内容进行解释执行找到Makefile中第一个目标对象,通过依赖对象的时间关系判断是否需要重新生成,若需要则执行命令,完毕后退出;

若依赖对象也需要生成,则会在下边的生成规则中找到依赖对象的生成规则,先生成依赖对象。

C++代码

#include <iostream>
using namespace std;

int main() {
    printf("hello Makefile!");
	return 0; 
}

Makefile文件

test:test.o
	g++ test.o -o test
test.o:test.s
	g++ -c test.s -o test.o
test.s:test.i
	g++ -S test.i -o test.s
test.i:test.cpp
	g++ -E test.cpp -o test.i

.PHONY:clean 
clean:
	rm -f hello.i hello.s hello.o hello

依赖关系

  • 上面的文件 test ,它依赖 test.o
  • test.o , 它依赖 test.s
  • test.s , 它依赖 test.i
  • hello.i , 它依赖 test.cpp

依赖方法

  • g++ test.* -option test.* ,就是与之对应的依赖关系

原理

make是如何工作的,在默认的方式下,也就是只输入make命令

  1. make会在当前目录下找名字叫“Makefile”或“makefile”的文件。

  2. 如果找到,它会找文件中第一个目标文件(target),在上述例子中,它会找到test这个文件,并将其作为最终的目标文件

  3. 如果test文件不存在,或是test所依赖的后面的test.o文件的修改时间要比test这个文件新,那么,它就会执行后面所定义的命令来生成test这个文件

  4. 如果test所依赖的test.o文件不存在,那么make会在当前文件中找目标为test.o文件的依赖性,如果找到则再根据那一个规则生成test.o文件(这有点像一个堆栈的过程)

  5. 当然,你的C文件和H文件是存在的,于是make会生成test.o文件,然后再用test.o文件声明make的终极任务,也就是执行文件hello

  6. 这就是整个make的依赖性,make会一层又一层的去找文件的依赖关系,直到最终编译出第一个目标文件

  7. 在寻找的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出并报错,而对于所定义的命令的错误,或是编译不成功,或是make根本不理会

  8. make只管文件的依赖性,即:如果在找了依赖关系后,毛好后面的文件还是不存在,那么它就不工作了

  9. 工程是要被清理的

  10. 像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令不会被自动执行,不过,我们可以显示要make执行,即命令:make clean,以此来清除所有目标文件,以便重新编译

  11. 一般我们要clean的目标文件,我们将它设置为伪目标,用.PHONY修饰,伪目标的特性是总是被执行的

Makefile的编写规则:

  1. 目标对象(要生成的文件名称)

  2. 依赖对象(用于判断目标对象是否最新是否需要重新生成)

  3. tab 命令(生成这个目标对象所要执行的命令,但是有时候这个命令也不一定非要生成目标对象)

预定义变量:
$< 第一个依赖文件
$^ 所有的依赖文件
[email protected] 目标文件

#[目标对象:要生成的可执行程序或者动态库或者静态库]:[依赖对象]
#     [执行的命令]gcc a.c -o a
#     [email protected]:[目标对象] $^:[所有的依赖的对象] $<:[第一个依赖的对象]

#a:a.o
#     gcc a.c -o a
#a.o:a.c
#     gcc -c a.c -o a.o
#
#a:a.c
#     gcc a.c b.c -o a
#
#a:a.c b.c
#     gcc $^ -o [email protected]

make的解释执行规则:

  1. make在Makefile中只找第一个目标对象,为了生成这个目标对象,而执行命令,完毕之后直接退出(后边的对象都不会在生成了)

  2. make在Makefile中找到第一个对象,这时候这个对象的依赖对象不存在,则在后续编译规则中,寻找是否可以生成这个依赖对象,当所有依赖对象生成完毕后,最终生成目标对象.

.PHONY的使用

.PHONY:$(target) 声明伪对象:使一个目标对象无论如何每次都需要重新生成

.PHONY 后面定义的是伪目标

所谓伪目标就是这样一个目标,它不代表一个真正的文件名,在执行make时可以指定这个目标来执行其所在规则定义的命令,有时我们将一个伪目标成为标签。

为什么要使用伪目标?

  1. 为了避免在makefile中定义的只执行命令的目标和工作目录下的实际文件出现名字冲突
  2. 为了提交执行makefile时的效率。

.PHONY配置项的主要作用在于避免指定命令和项目下的同名文件冲突,进行性能优化。.PHONY [command name] 显式指定了一个命令名称【而不是实际文件名】,是为了避免该命令名称与makefile同路径下的文件名重名冲突,以使make [command name]命令可以正确被解析、执行。 下面将举例说明。

如果编写一个规则名称为clean,不产生目标文件,那么该命令在执行make clean时规则都会被执行,makefile内容如下:

clean:
    rm -f hello.i hello.s hello.o hello

rm 命令不产生任何clean文件。

如果目录下不存在名为"clean"的文件,则运行make clean时,命令都会正常执行;
如果目录下存在名为"clean"的文件,则运行make clean时,命令规则会失效,文件"clean"没有依赖文件,始终是最新的,make clean永远不会被执行。

为了解决上述问题,可以使用.PHONY clean指明该clean命令。如下:

.PHONY clean
clean:
    rm -f *.o temp

这样执行命令make clean会自动忽略名为"clean"文件的存在。已知.PHONY配置项的目标并不是其他文件生成的实际文件,make命令会自动绕过隐含规则搜索过程,因此声明**.PHONY配置项会改善性能**,并且不需要担心实际同名文件存在与否。


如有不同见解,欢迎留言讨论~~

相关标签: Linux

上一篇: GNU make

下一篇: GNU Make 目录搜索