makefile 关系到整个工程的编译规则,它定义了一系列规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译。
makefile带来的好处就是“自动化编译”,一旦编好,只需要一个make命令就可以完成整个工程的编译,极大的提高了编译效率。
make是一个命令工具,是一个解释makefile中指令的命令工具。
无论是C还是C++都需要将文件编译成中间代码文件,在windows下是.obj文件,在UNIX下是.o文件,这个动作叫做编译(complie)。然后把大量的Object File文件合成为可执行文件,这个过程叫做链接。
在编译时,需要的是编译文件的语法正确。
在链接时,需要告诉编译器头文件所在的位置。
一般来来说,每个文件都应该对一个.o/.obj文件。
链接时,主要是链接函数和全局变量。
在大多时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显的指出中间目标文件名,这对于编译很不方便,所以我们要给中间目标文件打个包,在windows下这种包叫“库文件”,也就是.lib文件,在UNIX下,就是.a文件。
MakeFile规则
target ... : prerequisites
command
...
...
target 是目标文件,可以是Object File文件,也可以是执行文件,也可以是一个标签。
prerequisites 是要生成的target所需要的文件或者目标
command 就是make需要执行的命令。
这是一个文件依赖关系,也就是说,target 这一个或者多个目标文件依赖于prerequisites中的文件,其生成规则定义在command中。
edit : main.o kbd.o command.o display.o insert.o search.o files.o utils.o
cc -o edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o :command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
instert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o: files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean:
rm edit mian.o kbd,o command.o display.o insert.o search.o files.o utils.o
我们可以将这个文件保存为Makefile/makefile,然后在该目录下直接输入命令“make”就可以生成执行文件edit。如果要删除执行文件和所有的中间文件,那么只要简单的执行一下“make clean”就可以了。
在这个makefile 中,目标文件target 包含:
执行文件edit和中间目标文件(*.o)
依赖文件就是:后面的哪些.c和.h,每一个.o都有一组依赖文件,而这些.o又是执行文件 edit的依赖文件。依赖关系实质上就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。
在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个Tab键作为开头。make并不管命令是怎么工作的,他只管指令所定义的命令。make会比较targets文件和prerequisites文件的修改日期,如果prerequisites文件的日期要比targets文件的日期要新,或者target不存在的话,那么,make就会执行后续定义的命令。
clean 不是一个文件,他只不过是一个动作的名字,有点像C语言中的lable一样,其:后面什么也没有,make就不会自动取找文件依赖性,也就不会自动执行其后面所定义的命令。要执行后面定义的命令,就要在make命令后面明显的指出这个lable的名字。
make是如何工作的
当输入make命令后,
1.make会在当前目录下找名字为“Makefile”或“makefile”的文件
2.如果找到,他会找文件中的第一个目标文件(target),在上面的例子中,他会找到“edit”这个文件,并把这个文件作为最终的目标文件。
3.如果“edit”文件不存在,或是edit所依赖的后面的.o文件修改时间要比这个文件新,那么,他就会执行后面定义的命令来生成edit这个文件
4. 如果edit 所以依赖的.o文件也存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到再根据规则生成.o文件
5.当然,你的C文件和H文件是存在的,于是make会生成.o文件,再用生成的.o文件生成最终文件edit
makefile中变量使用
在上面的例子中相同的字符串被使用了多次,如果我们的工程中需要新加入一个新的.o文件,那么我们需要在多个地方添加。
为了makefile的容易维护,在makefie中我们可以使用变量。makefile变量可以理解为C语言中的宏。
声明一个变量objects
objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
于是之前的例子可以被修改为:
objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : main.c defs.h
cc -c main.c
kbd.o : kbd.c defs.h command.h
cc -c kbd.c
command.o :command.c defs.h command.h
cc -c command.c
display.o : display.c defs.h buffer.h
cc -c display.c
instert.o : insert.c defs.h buffer.h
cc -c insert.c
search.o : search.c defs.h buffer.h
cc -c search.c
files.o: files.c defs.h buffer.h command.h
cc -c files.c
utils.o : utils.c defs.h
cc -c utils.c
clean:
rm edit $(objects)
如果有新的.o文件加入,我们只需要简单的修改一下objects变量就可以了。
让make自动推导
GNU的make很强大,它可以自动推导文件以及文件依赖关系后面的命令,于是我们没有必要去在每一个.o文件后面都写上类似命令,因为,make会自动识别,并自动推导命令。
只要make 看到一个.o文件,他就会自动的把.c文件加在依赖关系中。例如,如果make找到一个whatever.o 那么,whatever.c就会是whatever.o的依赖文件。并且cc -C whatever.c 也会被推导出来。
于是上面的makefile可以修改为如下:
objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o
edit : $(objects)
cc -o edit $(objects)
main.o : defs.h
kbd.o : defs.h command.h
command.o :defs.h command.h
display.o : defs.h buffer.h
instert.o : defs.h buffer.h
search.o : defs.h buffer.h
files.o : defs.h buffer.h command.h
utils.o : defs.h
clean:
rm edit $(objects)
这种方法是make的一种隐晦规则
清空目标文件的规则
一般的做法是
clean:
rm edit $(objets)
更稳健的方式是
.PHONY: clean
clean:
-rm edit $(objects)
.PHONY 表示clean是一个伪目标,在rm前面加-的意思是,也许有些文件会出现问题,但不要管,继续后面的事。
clean 一般都是放在文件的最后。
Makefile总述
Makefile里有什么
makefile中主要包含了五个东西:显示规则、隐晦规则、变量定义、文件指示和注释
1.显示规则:
显示规则说明了如何生成一个或者多个目标文件。这是由makefile的书写者明显的指出,要生成的文件,文件的依赖文件,生成的命令。
2.隐晦规则
由于我们的make有自动推导的功能,所以隐晦的规则可以让我们比较粗糙的简略的书写makefile
3.变量的定义
在makefile中我们要定义一系列的变量,变量一般都是字符串,相当于C语言中的宏定义,当makefile执行时,其中的变量就会被扩展到相应的引用位置上。
4.文件指示
其中包括三个部分,一个是在makefile中引用另一个Makefile,就像C语言中的include 一样,另一个是指根据某些指定的makefile中的有效部分,就像C语言中的条件编译一样,还有就是定义一个多行命令。
5.注释
Makefile中只有行注释,和UNIX的shell脚本一样,其注释是用“#”,如果要在makefile中使用 # 那么按 \# 转义使用
makefile的文件名
默认情况下,make命令会在当前目录下按顺序找寻 GNUmakefile makefile Makefile文件,找到了解释这个文件。
引用其他的makefile
在Makefile中使用include 关键字可以把别的Makefile包含进来,这很像C语言的#include ,被包含的文件会原模原样的放在当前文件的包含位置。 include <filename>
filename可以是当前操作系统和shell的文件模式,在include前面 可以有一些空字符,但是不能以TAB键开始。
include foo.make *.mk $(bar)
等价于:
include foo.make a.mk b.mk c.mk e.mk f.mk
make命令开始时,会找寻include所指出的其他Makefile,并把其内容安置在当前的位。就好像C指令一样,如果文件都没有指定绝对路的话,make会在当前目录下首先寻找,如果当前目录下没有找到,那么,make 还会再下面的目录中寻找:
1.如果make执行时,“有-I”或者“--include-dir”参数,那么make就会在这个参数所指定的目录下去寻找。
2.如果目录 <prefix>/include 存在的话,make也会去找。如果没有找到,make会生成一条警告信息,但不会立马出现致命错误。他会继续载入其它的文件,一旦完成makefile的读取,make会再重试这些没有找到,或读取不到的文件,如果还是不行,make才会出现一条致命信息。如果想让make不理会哪些无法读取到的文件而继续执行,可以再include 前增加一个 “-”
环境变量 MAKEFILES
如果当前环境中定义了环境变量MAKEFILES,那么,make会把这个变量中的值做一个类似include 的动作。这个变量中的值是其它Makefile,用空格分开。他和include 不同的是,从这个环境变量中引入的Makefile的目标不会起作用,如果环境变量发生错误,make也不会理。
尽量建议不要使用这个环境变量。
make的工作方式
1.读取所有的Makefile
2.读入被include的其他makefile
3.初始化文件中的变量
4.推导隐晦规则,并分析素所有规则。
5.为所有的目标文件创建依赖关系链
6.根据依赖关系,决定哪些目标要重新生成
7.执行生成命令
书写规则
规则包括两个部分,依赖关系 和目标生成方法。
1,规则举例
foo.o : foo.c defs.h #foo 模块
cc -c -g foo.c
看到这个例子,各位应该不会很陌生,前面说过,foo.o是我们的目标,foo.c和defs.h是目标所依赖的源文件,而只有一个命令
“cc -c -g foo.c”
这个规则告诉我们两件事:
1.文件依赖关系,foo.o依赖于文件foo.c和文件defs.h的文件,如果foo.c和defs.h的文件日期要比foo.o的日期要新,或者foo.o不存在,那么发生依赖关系。
2.如何生成或者更新的foo.o文件,也就是这个cc命令,其说明了,如何生成foo.o这个文件。