前情提要
上一篇《编译入门》讲了变成的基本问题。如果源文件只有一个,就如之前的例子,那么用gcc命令直接编译就可以了。但是很多实际的工程用到的源文件都是相当多的,这时候用命令一个个编译是很不现实的。所以需要一个自动化编译系统来做这件事情,那就是make和makefile了。
make和makefile介绍
一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile 定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为 makefile 就像一个 Shell 脚本一样,其中也可以执行操作系统的命令。
make 是一个命令工具,是一个解释makefile 中指令的命令工具,一般来说,大多数的 IDE 都有这个命令,比如:Delphi 的make,Visual C++的 nmake,Linux 下 GNU 的 make。可见,makefile 都成为了一种在工程方面的编译方法。
make和makefile的示例:它们是如何工作的
我们的工程有 8 个 C 文件,和 3 个头文件,我们要写一个 Makefile 来告诉 make 命令如何编译和链接这几个文件。我们要写一个 Makefile 来告诉 make 命令如何编译和链接这几个文件。我们的规则是:
1. 如果这个工程没有编译过,那么我们的所有 C 文件都要编译并被链接。
2. 如果这个工程的某几个 C 文件被修改,那么我们只编译被修改的 C 文件,并链接目标程序。
3. 如果这个工程的头文件被改变了,那么我们需要编译引用了这几个头文件的 C 文件,并链接目标程序。
在讲述这个 Makefile 之前,还是让我们先来粗略地看一看 Makefile 的规则。
target ... : prerequisites ...
command
...
...
target 也就是一个目标文件,可以是 Object File,也可以是执行文件。还可以是一个标签(Label),对于标签这种特性,在后续的“伪目标”章节中会有叙述。
prerequisites 就是,要生成那个 target 所需要的文件或是目标。
command 也就是 make 需要执行的命令。(任意的 Shell 命令)
这是一个文件的依赖关系,也就是说,target这一个或多个的目标文件依赖于prerequisites中的文件,其生成规则定义在command中。说白一点就是说,prerequisites中如果有一个以上的文件比 target 文件要新的话, command 所定义的命令就会被执行。
这就是 Makefile 的规则。也就是 Makefile 中最核心的内容。
以下是makefile的代码。
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
insert.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 main.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),依赖文件(prerequisites)就是冒号后面的那些 .c 文件和 .h 文件。每一个 .o 文件都有一组依赖文件,而这些 .o 文件又是执行文件 edit 的依赖文件。依赖关系的实质上就是说明了目标文件是由哪些文件生成的,换言之,目标文件是哪些文件更新的。
在定义好依赖关系后,后续的那一行定义了如何生成目标文件的操作系统命令,一定要以一个 Tab 键作为开头。记住, make 并不管命令是怎么工作的,他只管执行所定义的命令。 make会比较 targets 文件和 prerequisites 文件的修改日期,如果 prerequisites 文件的日期要比 targets 文件的日期要新,或者 target 不存在的话,那么, make 就会执行后续定义的命令。
这里要说明一点的是, clean 不是一个文件,它只不过是一个动作名字,有点像 C 语言中的lable 一样,其冒号后什么也没有,那么, make 就不会自动去找文件的依赖性,也就不会自动执行其后所定义的命令。要执行其后的命令,就要在 make 命令后明显得指出这个 lable的名字。这样的方法非常有用,我们可以在一个 makefile 中定义不用的编译或是和编译无关的命令,比如程序的打包,程序的备份,等等。
上面是makfile的核心,但还有很多很多其他的细节,慢慢道来。
参考文献: 跟我一起写makefile