先引用一段经典的makefile名言————》什么是makefile?或许很多Winodws的程序员都不知道这个东西,因为那些Windows的IDE都为你做了这个工作,但我觉得要作一个好的和professional的程序员,makefile还是要懂。这就好像现在有这么多的HTML的编辑器,但如果你想成为一个专业人士,你还是要了解HTML的标识的含义。特别在Unix下的软件编译,你就不能不自己写makefile了,会不会写makefile,从一个侧面说明了一个人是否具备完成大型工程的能力。
因为,makefile关系到了整个工程的编译规则。一个工程中的源文件不计数,其按类型、功能、模块分别放在若干个目录中,makefile定义了一系列的规则来指定,哪些文件需要先编译,哪些文件需要后编译,哪些文件需要重新编译,甚至于进行更复杂的功能操作,因为makefile就像一个Shell脚本一样,其中也可以执行操作系统的命令。
makefile带来的好处就是——“自动化编译”,一旦写好,只需要一个make命令,整个工程完全自动编译,极大的提高了软件开发的效率。make是一个命令工具,是一个解释makefile中指令的命令工具,一般来说,大多数的IDE都有这个命令,比如:Delphi的make,Visual C++的nmake,Linux下GNU的make。可见,makefile都成为了一种在工程方面的编译方法。
本实例使用的又以下几个文件(foo.h foo1.c foo2.c 和mian.c)
foo.h内容如下:
void foo1(void);
void foo2(void);
foo1.c内容如下:
#include <stdio.h>
void foo1(void)
{
printf("%s:+start\n",__FUNCTION__);
printf(" foo1 called\n");
printf("%s:+end\n",__FUNCTION__);
}
主要作用打印函数名
foo2.cde 内容:
#include <stdio.h>
void foo2(void)
{
printf("%s:+start\n",__FUNCTION__);
printf(" foo2 called\n");
printf("%s:+end\n",__FUNCTION__);
}
main.c内容:
#include <stdio.h>
#include "foo.h"
#include "foo1.h"
int main()
{
printf("%s:+start\n",__FUNCTION__);
foo1();
foo2();
printf("%s:-end\n",__FUNCTION__);
}
写一个最简单的makefile:
main : main.o foo1.o foo2.o
gcc -o main main.o foo1.o foo2.o
main.o : main.c
gcc -c main.c
foo1.o : foo1.c
gcc -c foo1.c
foo2.o : foo2.c
gcc -c foo2.c
clean:
rm -f *.o
按照makefile手册所说makefile终极目标是:默认的情况下,make执行的是Makefile中的第一个规则,此规则的第一个目标称之为“最终目的”或者“终极目标”(就是一个Makefile最终需要更新或者创建的目标。
而一个规则标准定义如下:
TARGET… : PREREQUISITES…
COMMAND
target:规则的目标。通常是最后需要生成的文件名或者为了实现这个目的而必需
的中间过程文件名。可以是.o文件、也可以是最后的可执行程序的文件名等。另外,目
标也可以是一个make执行的动作的名称,如目标“clean”,我们称这样的目标是“伪
目标”。
prerequisites:规则的依赖。生成规则目标所需要的文件名列表。通常一个目标依
赖于一个或者多个文件。
command:规则的命令行。是规则所要执行的动作(任意的shell命令或者是可在
shell下执行的程序)。它限定了make执行这条规则时所需要的动作。
一个规则可以有多个命令行,每一条命令占一行。注意:每一个命令行必须以[Tab]
字符开始,[Tab]字符告诉make此行是一个命令行。make按照命令完成相应的动作。
这也是书写Makefile中容易产生,而且比较隐蔽的错误。
命令就是在任何一个目标的依赖文件发生变化后重建目标的动作描述。一个目标可
以没有依赖而只有动作(指定的命令)。比如Makefile中的目标“clean”,此目标没有
依赖,只有命令。它所定义的命令用来删除make过程产生的中间文件(进行清理工作)。
在Makefile中“规则”就是描述在什么情况下、如何重建规则的目标文件,通常
规则中包括了目标的依赖关系(目标的依赖文件)和重建目标的命令。make执行重建
目标的命令,来创建或者重建规则的目标(此目标文件也可以是触发这个规则的上一个
规则中的依赖文件)。规则包含了文件之间的依赖关系和更新此规则目标所需要的命令。
一个Makefile文件中通常还包含了除规则以外的很多东西(后续我们会一步一步
的展开)。一个最简单的Makefile可能只包含规则。规则在有些Makefile中可能看起来
非常复杂,但是无论规则的书写是多么的复杂,它都符合规则的基本格式。
make程序根据规则的依赖关系,决定是否执行规则所定义的命令的过程我们称之
为执行规则。
在上个makefile中main main.o foo1.o 和foo2.o都是目标。但是main是终极目标因为main是makefile中最先出现的目标。
在中断下执行make命令结果如下:
make
gcc -c main.c
gcc -c foo1.c
gcc -c foo2.c
gcc -o main main.o foo1.o foo2.o
执行文件
./main
main:+start
foo1:+start
foo1 called
foo1:+end
foo2:+start
foo2 called
foo2:+end
main:-end
有没有发现main这个目标没有依赖clean,所以编译的时候也不会执行clean这个规则,但是我想执行这个规则咋办,很简单直接执行
make clean
rm -f *.o
make 加上 规则目标就可以执行特定的目标。(也可以make foo1.o等等)
一个规则可以有多个命令执行比如我下面修改多加一个
main : main.o foo1.o foo2.o
gcc -o main main.o foo1.o foo2.o
mv main main1
main.o : main.c
gcc -c main.c
foo1.o : foo1.c
gcc -c foo1.c
foo2.o : foo2.c
gcc -c foo2.c
clean:
rm -f *.o main1 main
多加一个cp main main1
当然也可以写成一行
main : main.o foo1.o foo2.o
gcc -o main main.o foo1.o foo2.o ;mv main main1
结果:
gcc -c main.c
gcc -c foo1.c
gcc -c foo2.c
gcc -o main main.o foo1.o foo2.o
mv main main1
是不是感觉上个makefile写的很挫!!我们可以修改一下顺便说说makefile其他用法.
main : main.o foo1.o foo2.o
gcc -o main main.o foo1.o foo2.o
main.o :
foo1.o :
foo2.o :
clean:
rm -f *.o main1 main
makefile规则含有隐式推倒,遇到 var.o 目标就会找var.c文件,遇到 main.o就会找main.c文件
上面还是有点不简单,下面好看多了
main : main.o foo1.o foo2.o
gcc -o main main.o foo1.o foo2.o
clean:
rm -f *.o main1 main
每次都写 main.o foo1.o foo2.o还是很多,假如在加一个foo3 岂不是每个都加。在编译大型程序,那么多文件很容易出错,修改后如下:
obj= main.o foo1.o foo2.o
main : $(obj)
gcc -o main $(obj)
clean:
rm -f *.o main1 main
这个加一个文件直接在 obj后面添加就可以了。
在当前目录新建文件夹src 把 foo2.c移动到src下,
main : main.o foo1.o foo2.o
gcc -o main main.o foo1.o foo2.o
main.o : main.c
gcc -c main.c
foo1.o : foo1.c
gcc -c foo1.c
foo2.o : foo2.c
gcc -c foo2.c
clean:
rm -f *.o main1 main
发现编译有错误,修改后
foo2.o : ./src/foo2.c
gcc -c ./src/foo2.c
就可以了。
这样还是有点不雅观!!在修改一下:
vpath %.c src
main : main.o foo1.o foo2.o
gcc -o main main.o foo1.o foo2.o
main.o : main.c
gcc -c main.c
foo1.o : foo1.c
gcc -c foo1.c
foo2.o : foo2.c
gcc -c $<
clean:
rm -f *.o main1 main
vpath %.c src 表示 如果在当前目录下找不到.c文件就去子目录src下去找,
foo2.o : foo2.c
gcc -c $<
不可写成
foo2.o : foo2.c
gcc -c foo2.c
因为 foo2.c 是一个定量,指定死了文件,所以要用自动化变量,让makefile自动搜索