make是一个命令,makefile是一个文件,通常该文件会在当前工作目录下存放。
在VS当中,项目管理(多文件管理)是VS帮我们自动管理的。
但是在Linux中,维护项目文件关系的工作(比如同时编译链接多个文件),需要让make、makefile来完成。所以makefile带来的好处就是“自动化编译”,一旦写好,只需一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。
文章目录
依赖关系和依赖方法
如果对A文件作出修改会影响文件B,那B就依赖于A,比如B是由文件A编译链接出来的。
以链表举例子,一个可执行程序list
包含三个list.c、list.h、main.c
,最终生成的可执行程序是由list.o
和main.o
所链接的,所以list
依赖于list.o
和main.o
。而list.c
和main.o
又是各自的.c
文件通过预处理、编译以及汇编得到的,.c
文件的改变会影响.o
,所以说.o
文件依赖于.c
文件。
如果文件B依赖于文件A,那么通过文件A得到文件B的方法,就是文件B依赖于文件A的依赖方法。
比如将list.o
和main.o
链接形成list
就是依赖方法。.c
文件通过预处理、编译以及汇编得到.o
文件就是依赖方法。
使用Makefile实现多文件编译
以链表为例:
我们大可以直接对多个源文件进行编译,进而生成可执行程序。:
但进行多文件编译的时候一般不使用源文件直接生成可执行程序,而是先用每个源文件各自生成自己的二进制文件,然后再将这些二进制文件通过链接生成可执行程序。
原因有两点:
- 如果直接使用源文件生成可执行程序,那么其中一个源文件进行了修改,再生成可执行程序的时候就需要将所以的源文件重新进行编译链接。
- 如果是先用每个源文件各自生成自己的二进制文件,那么其中一个源文件进行了修改,就只需重新编译生成该源文件的二进制文件,然后再将这些二进制文件通过链接生成可执行程序即可。
就像VS会生成.obj
文件一样:
另外, 编译链接的时候不需要加上头文件,因为编译器通过源文件的内容可以知道所需的头文件名字,而通过头文件的包含方式(<>包含和" ”包含),链接的时候编译器就知道应该从何处去寻找所需的头文件。
上面的方法对于文件比较少的情况还好,当文件比较多时(比如上百个),那gcc后面跟的文件个数就多了,而且也会有漏掉或写错的可能。很明显这种方式是不好的。
此时使用make和Makefile了,将会大大减工作量:
- 在源文件所在目录下创建一个名为Makefile的文件
- 编写Makefile文件,编写的规则包括两部分:依赖关系和依赖方法。通常先写出文件的依赖关系,然后写出这些文件之间的依赖方法,依次写下去:
比如这里list依赖list.o和main.o,而依赖方法就是将通过gcc将main.o和list.o链接起来,同时list.o依赖list.c,main,o依赖main.c
- 编写完成后执行make即可
上面的写法还是非常繁琐,因为gcc后还要写各种文件,使用通配符可以避免这种情况:
-
$@
:表示依赖关系中的目标文件(冒号左侧)。 -
$^
:表示依赖关系中的依赖文件列表(冒号右侧全部)。 -
$<
:表示依赖关系中的第一个依赖文件(冒号右侧第一个)。 -
%
:表示同类型的文件。
所以Makefile就可以这样写:
使用通配符%,可以将大量同类型的文件,只用一条规则就完成构建。
%.o:%.c
等价于list.o:list.c
和main.o:main.c
make的原理:
- make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
- 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“list”这个文件,并把这个文件作为最终的目标文件。
- 如果list文件不存在,或是list所依赖的后面的list.o和main.o文件的文件修改时间要比list这个文件新,那么,他就会执行后面所定义的命令来生成list这个文件。
- 如果list所依赖的.o文件不存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。
- 当然,.c文件和.h文件是存在的啦,于是make会生成 list.o和main.o 文件,然后再用 这俩.o 文件声明make的终极任务,也就是执行文件list了。
- 这就是整个make的依赖性,make会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。
- 在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么make就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make根本不理。
- make只管文件的依赖性,即,如果在找了依赖关系之后,冒号后面的文件还是不在,那么make就不工作。
使用Makefile清理项目
也可以使用Makefile清理项目:clean
被 .PHONY
修饰成伪目标,不是真实存在的文件clean。
像clean这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,
所以要用make执行。即命令make clean
,以此来清除所有的目标文件,以便重编译。
伪目标的特性是,总是被执行的。如果clean
不被 .PHONY
修饰成伪目标,那么当前目录存在一个clean文件的时候,make clean
这条命令就不会被执行:
综上,Makefile全部内容如下: