关于makefile的规则

来源:互联网 发布:淘宝店铺类目 编辑:程序博客网 时间:2024/05/20 02:27

链接

链接器只管目标文件(Object File),在大多数时候,由于源文件太多,编译生成的中间目标文件太多,而在链接时需要明显地指出中间目标文件名,这对于编译很不方便,所以,我们要给中间目标文件打个包,在 Windows 下这种包叫“库文件”(Library File),也就是 .lib 文件,在 UNIX 下,是 Archive File,也就是 .a 文件。

  1. 如果这个工程没有编译过,那么所有 C 文件都要被编译和链接;
  2. 如果这个工程的某几个 C 文件被修改,那么只编译被修改的 C 文件,并链接目标程序;
  3. 如果这个工程的头文件被改变了,那么需要编译引用了这几个头文件的 C 文件,并链接目标程序。
target ... : prerequisites ... command ...

target是一个目标文件(可以是Object File,执行文件或者一个标签)
prerequisites:是要生成那个target所需要的文件或目标
command:需要执行的命令

这是一个文件的依赖关系,也就是说,target 这一个或多个的目标文件依赖于 prerequisites 中的文件,其生成规则定义在 command 中。说白一点就是说, prerequisites 中如果有一个以上的文件比 target 文件要
新的话,command 所定义的命令就会被执行。

示例

如果一个工程有 3 个头文件,和 8 个 C 文件,我们为了完成前面所述的那三个规则,我们的 Makefile 应该是下面的这个样子的。

edit : main.o kbd.o command.o display.o insert.o search.o files.o utils.occ -o edit main.o kbd.o command.o display.o insert.o search.o files.o utils.omain.o : main.c defs.hcc -c main.ckbd.o : kbd.c defs.h command.hcc -c kbd.ccommand.o : command.c defs.h command.hcc -c command.cdisplay.o : display.c defs.h buffer.hcc -c display.cinsert.o : insert.c defs.h buffer.hcc -c insert.csearch.o : search.c defs.h buffer.hcc -c search.cfiles.o : files.c defs.h buffer.h command.hcc -c files.cutils.o : utils.c defs.hcc -c utils.cclean :rm edit main.o kbd.o command.o display.o insert.o search.o files.o utils.o

在该目录下直接输入命令“make”就可以生成执行文件 edit。如果要删除执行文件和所有的中间目标文件, 那么, 只要简单地执行一下“make clean”就可以了。

在这个 makefile 中,目标文件(target)包含:执行文件 edit 和中间目标文件(*.o),依赖文件(prerequisites)就是冒号后面的那些 .c 文件和 .h 文件。每一个 .o 文件都有一组依赖文件,而这些 .o 文件又是执行文件 edit 的依赖文件。依赖关系的实质上就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。

在定义好依赖关系后, 后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个 Tab 键作为开头。记住,make 并不管命令
是怎么工作的,他只管执行所定义的命令。make 会比较 targets 文件和 prerequisites 文件的修改日期,如果 prerequisites 文件的日期比targets 文件的日期新,或者 target 不存在的话,那么,make 就会执行后续定义的命令。

clean 不是一个文件,它只不过是一个动作名字,有点像 C 语言中的 lable 一样,其冒号后什么也没有,那么,make就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在 make 命令后明显得指出这个 lable的名字。这样的方法非常有用,我们可以在一个 makefile 中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。

make的工作流程

输入make命令后的执行流程:

  1. make 会在当前目录下找名字叫 “Makefile” 或 “makefile” 的文件;
  2. 如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到 “edit” 这个文件,并把这个文件作为最终的目标文件;
  3. 如果 edit 文件不存在,或是 edit 所依赖的后面的 .o 文件的文件修改时间要比 edit 这个文件新,那么,他就会执行后面所定义的命令来生成 edit 这个文件;
  4. 如果 edit 所依赖的.o 文件也存在,那么 make 会在当前文件中找目标为.o 文件的依赖性,如果找到则再根据那一个规则生成.o 文件。(这有点像一个堆栈的过程);
  5. 当然,你的 C 文件和 H 文件是存在的啦,于是 make 会生成 .o 文件,然后再用 .o 文件生命 make 的终极任务,也就是执行文件 edit了。

这就是整个 make 的依赖性, make 会一层又一层地去找文件的依赖关系,直到最终编译出第一个目标文件。在找寻的过程中,如果出现错误,比如最后被依赖的文件找不到,那么 make 就会直接退出,并报错,而对于所定义的命令的错误,或是编译不成功,make 根本不理。make 只管文件的依赖性,即,如果在我找了依赖关系之后,冒号后面的文件还是不在,那么对不起,我就不工作啦。

伪目标:并不对应于实际的文件,只规定了make应该执行的命令(因为没有依赖体,make 会认为目标体是最新的而不执行任何操作)
像 clean 这种,没有被第一个目标文件直接或间接关联,那么它后面所定义的命令将不会被自动执行,不过,我们可以显示要 make 执行。即命令——make clean,以此来清除所有的目标文件,以便重编译。

makefile 的变量也就是一个字符串

#定义变量objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.o #以“$(objects)”的方式来使用变量edit : $(objects)cc -o edit $(objects)main.o : main.c defs.hcc -c main.ckbd.o : kbd.c defs.h command.hcc -c kbd.ccommand.o : command.c defs.h command.hcc -c command.cdisplay.o : display.c defs.h buffer.hcc -c display.cinsert.o : insert.c defs.h buffer.hcc -c insert.csearch.o : search.c defs.h buffer.hcc -c search.cfiles.o : files.c defs.h buffer.h command.hcc -c files.cutils.o : utils.c defs.hcc -c utils.cclean :rm edit $(objects)

关于make的自动推导

GNU 的 make 可以自动推导文件以及文件依赖关系后面的命令。只要 make 看到一个[.o]文件,它就会自动的把[.c]文件加在依赖关系中,也就是说,如果 make 找到一个 whatever.o,那么 whatever.c 就会是 whatever.o 的依赖文件,并且 cc -c whatever.c 也会被推导出来。

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.oedit : $(objects)cc -o edit $(objects)main.o : defs.hkbd.o : defs.h command.hcommand.o : defs.h command.hdisplay.o : defs.h buffer.hinsert.o : defs.h buffer.hsearch.o : defs.h buffer.hfiles.o : defs.h buffer.h command.hutils.o : defs.h.PHONY : cleanclean :rm edit $(objects)

上面文件内容中,.PHONY表示,clean 是个伪目标文件
上面的makefile中有一堆重复的.h文件,可以使用自动推导功能,把重复的.h文件收拢起来。

objects = main.o kbd.o command.o display.o insert.o search.o files.o utils.oedit : $(objects)cc -o edit $(objects)$(objects) : defs.hkbd.o command.o files.o : command.hdisplay.o insert.o search.o files.o : buffer.h.PHONY : cleanclean :rm edit $(objects)

每个 Makefile 中都应该写一个清空目标文件(.o 和执行文件)的规则,这不仅便于重编译,也很利于保持文件的清洁。

.PHONY : cleanclean :-rm edit $(objects)

.PHONY 意思表示 clean 是一个“伪目标”,而在 rm 命令前面加了一个小减号的意思就是, 也许某些文件出现问题, 但不要管,继续做后面的事。当然,clean 的规则不要放在文件的开头,不然,这就会变成 make 的默认目标,相信谁也不愿意这样。不成文的规矩是——“clean 从来都是放在文件的最后”。

Makefile 里主要包含了五个东西:显式规则、隐晦规则、变量定义、文件指示和注释。

  1. 显式规则: 显式规则说明了,如何生成一个或多的的目标文件。
    这是由 Makefile 的书写者明显指出,要生成的文件,文件的依赖文件,生成的命令。
  2. 隐晦规则: 由于我们的 make 有自动推导的功能,所以隐晦的规则可以让我们比较粗糙地简略地书写 Makefile,这是由 make 所支持的。
  3. 变量的定义:在 Makefile 中我们要定义一系列的变量,变量一般都是字符串,这个有点你 C 语言中的宏,当 Makefile 被执行时,其中的变量都会被扩展到相应的引用位置上。
  4. 文件指示:其包括了三个部分,一个是在一个 Makefile 中引用另一个 Makefile,就像 C 语言中的 include 一样;另一个是指根据某些情况指定 Makefile 中的有效部分,就像 C 语言中的预编译#if 一样;还有就是定义一个多行的命令。有关这一部分的内容,我会在后续的部分中讲述。
  5. 注释:Makefile 中只有行注释,和 UNIX 的 Shell 脚本一样,其注释是用“#”字符,这个就像 C/C++中的“//”一样。如果你要在你的Makefile 中使用“#”字符,可以用反斜框进行转义,如:“#”。

PS:在 Makefile 中的命令,必须要以[Tab]键开始

0 0