makefile文件由一组依赖关系和规则构成。每个依赖关系由一个目标(一般为可执行文件)和要创建这个目标所需依赖的一组源文件构成。而规则定义了目标的创建方式。
依赖关系的写法是:目标名称+冒号+空格或制表符tab,最后是由空格或制表符隔开的文件列表,如:
myapp: main.o b.o f.o
main.o: main.c a.h
b.o: b.c a.h b.h
f.o: f.c b.h f.h
make命令假设在makefile文件中的第一个目标myapp是想创建的目标文件,确定创建所需依赖的源文件,先检查有没有现成的文件,再看makefile文件中有没有说明如何创建该文件,如果都没有则将报告一个错误。这样找到目标文件那一条线上的所有文件。同时make命令能自行判断出创建所需文件的正确顺序。
还可以定义伪目标如 all: myapp myall.1,就将创建myapp以及其帮助文件。
规则:
首先提一条可能有点奇怪的makefile语法,就是空格和tab是有区别的,规则所在的行必须以tab开头,用空格将导致make不能正常工作。这有历史上的原因。
大多数规则都会包含一个简单的命令,该命令是可以在命令行窗口执行的。
myapp: main.o b.o f.o
gcc -o myapp main.o b.o f.o
main.o: main.c a.h
gcc -c main.c
b.o: b.c a.h b.h
gcc -c b.c
f.o: f.c b.h f.h
gcc -c f.c
如果我们想通过make实现:清除make过程中产生的中间文件、安装目标至指定目录下等等任务,可以在Makefile文件末尾加如下:
clean:
-rm main.o b.o.f.o
install: myapp
@if [ -d $(INSTDIR) ]; \
then \
cp myapp $(INSTDIR);\
chmod a+x $(INSTDIR)/myapp;\ (设置所有用户可执行)
chmod og-w $(INSTDIR)/myapp;\ (设置去除other用户和同组用户的可写权限)
echo "Installed in $(INSTDIR)";\
else \
echo "sorry, $(INSTDIR) does not exist";\
fi
增加了2个新目标clean和install。
"clean: "表示没有依赖关系定义,只定义了规则。rm前带减号-,可让make命令忽略rm命令的执行结果,即使rm出错(如删除对象不存在)也不会返回和显示error,make clean都会成功。
install目标需要依赖myapp,因此必须先创建myapp。从myapp到install目标是通过一段shell脚本完成的。每行代码以反斜杠\结束,是使所有shell脚本命令在逻辑上处于同一行,作为一个整体传递给一个shell执行。同时以符号@开头可以使得make在执行这条规则前不会在stdout显示这些命令本身。
这其中有些脚本执行的执行需要你先具有root权限。
make命令最常用的3个选项是:
-f <makefilename>: 指定进行make所需的makefile文件名。默认会自动查找匹配名为makefile的文件,如找不到则再找名为Makefile的文件。Makefile也是最多人采用的命名,因为它也会出现在所在目标文件列表首位。如:make -f Makefile123。
-k 出现错误时仍然继续执行。该命令可以一次列出所有未成功编译的源文件。
-n 让make命令在正式执行操作前输出将要执行的操作步骤。
make命令的常用宏定义:
$@ 当前目标文件名
$< 当前所依赖的源文件的名称
$* 当前所依赖的源文件的名称(不带后缀名)
$? 当前目标所依赖的源文件列表中比当前目标文件还要新的文件
同时常会看到下面2个特殊字符:
-: 表示让make命令忽略所有错误。
@: 告诉make指令在执行某命令前不在标准输出显示该命令。@结合echo很有用。
内置规则
以上都对创建目标文件的规则(每个操作步骤)都做出了精确定义。其实,make命令自带有大量的内置规则,这些内置规则可以极大简化makefile文件中的内容。可以将文件makefile中用于制作目标的规则去掉,而只需指定依赖关系。makefile文件将相对变得很简单,如以下相应部分:
main.o: main.c a.h
b.o: b.c a.h b.h
f.o: f.c b.h f.h
make使用.<old_suffix>.<new_suffix>来定义一条通用规则,可以从旧后缀名文件创建新后缀名文件。如:
.cpp.o
$(CC) -xc++ $(CFLAGS) -I$(INCLUDE) -c $<
这里的$<表示具体使用时的依赖源文件(旧后缀名文件)的名称(带后缀名)。-xc++是告诉gcc编译器源文件是一个C++文件。
make用于函数库管理的语法是lib(file.o),用于将file.o文件合并入lib.a文件中,其内置规则常见形式如下:
.c.a:
$(CC) -c $(CFLAGS) $< $(AR)
$(ARFLAGS) $@ $*.o
第一条规则是编译源文件生成目标文件;第二条规则中宏$(AR)和$(ARFLAGS)的默认取值通常是命令ar和选项rv,表示用ar命令将新的目标文件添加到函数库中。
对于大型的项目,我们往往会将各个函数库下的源文件分不同的子目录存放。使用make完成这一工作的方法有2个:
1.在子目录中编写单独的makefile文件,用于编译该子目录下的源文件,并将它们保存到一个函数库中,最后将该库文件复制到上一级目录中:
mylib.a:
(cd mylib_directory;$(MAKE))
此后,执行make mylib.a,命令将首先切换到子目录下,并执行make命令。这两个命令被括号括起来,表示确保它们用同一个shell处理。
2.定义一些新宏,通过在已知宏($@, $<)的尾部追加字母得到:字母D表示目录,字母F表示文件,并用如下的规则替换相应的内置规则:
.c.o
$(CC) $(CFLAGS) -c $(@D)/$(<F) -o $(@D)/$(@F)
以上表示编译子目录中的源文件并将生成的目标文件置于子目录下。
接着进一步定义:
mylib.a: mylib/b.o mylib/f.o
ar -rv mylib.a $?
则函数库将被更新。
利用gcc -MM和makedepend工具等都可以自动生成依赖关系清单。