k;
这是一个简单的加法程序,包含两个文件:sum.c 和 add.c,其中,sum.c 中的 main 函数调用了 add.c 中的 add 函数。这个程序的依赖关系表如下:
sum: sum.o add.o
sum.o: sum.c stdio.h stdlib.h
add.o: add.c stdio.h
其中,最终所需要的目标文件是 sum,sum.o 和 add.o 是依赖 —— 要生成目标文件 sum ,需要先生成 sum.o 和 add.o。同样的,作为目标的 sum.o 依赖于 sum.c、stdio.h 和 stdlib.h;add.o 依赖于 add.c 和 stdio.h。这组依赖关系形成了一个层次结构,它显示了源文件之间的关系。
可以看出来,如果 add.c 发生了改变,那么就需要重新编译 add.o,而由于 add.o 发生了改变,目标文件 sum 也需要被重新创建,同时,由于 add.c 的改变并没有影响到 sum.o(sum.o 不依赖于 add.c),因此,sum.o 并不需要被重新编译。也就是说,通过使用 makefile 文件和 make 命令,我们可以实现,只重新编译所有受到改动影响的源文件,没有受到影响的源文件不必重新编译。这比把整个程序全部重新编译一遍显然要快上很多,尤其是对于大型程序。
3.2 规则
makefile 文件里另一部分内容是规则,它们定义了目标的创建方式。 规则的内容可以是任意的 shell 命令。关于规则,有以下两点需要注意:
1)规则所在行必须以制表符 tab 开头,不能用空格;
2)规则所在行最好不要以空格结尾,可能会导致 make 命令执行失败;
3)如果一行不足以写下所有内容,需要在每行代码的结尾加上一个反斜杠符 “\”,以让所有的命令在逻辑上处于同一行。
两个特殊字符 - 和 @:
1)在规则中,若命令之前加上了符号 “-”,则表明 make 命令将忽略该命令产生的所有错误;
2)若在命令之前加上了符号“@”,则表明 make 在执行该命令前,不会将该命令显示在标准输出上。
/* Makefile */
all: sum
sum: sum.o add.o
gcc -o sum add.o sum.o
sum.o: sum.c
gcc -c sum.c
add.o: add.c
gcc -c add.c
clean:
-rm sum sum.o add.o
这是 3.1 中 sum.c 程序的 makefile 文件。其中 gcc 、rm 命令等行就是规则,它们告诉了 make 命令将如何去创建目标。
两个特殊的目标:clean 和 install
目标 clean 和 install 是两个特殊的目标,它们并不用于创建文件,而是有其他用途。
目标 clean 在前面已经提到过,它使用 rm 命令来删除目标文件。rm 命令通常以减号 - 开头,表示让 make 命令忽略该命令的执行结果,这意味着,即使由于文件不存在而导致 rm 命令返回错误,命令 make clean 也能成功执行。
目标 install 用于按照命令的执行顺序将应用程序安装到指定的目录,还是用上面的 sum.c 程序来演示一下目标 install 的用法:
all: sum
# 安装目录
INSTDIR = /tmp
sum: sum.o add.o
gcc -o sum add.o sum.o
sum.o: sum.c
gcc -c sum.c
add.o: add.c
gcc -c add.c
clean:
rm sum sum.o add.o
install: sum
@if [ -d $(INSTDIR) ];\
then\
cp sum $(INSTDIR);\
chmod a+x $(INSTDIR)/sum;\
chmod og-w $(INSTDIR)/sum;\
echo "Installed in $(INSTDIR)";\
else\
echo "The directory $(INSTDIR) dose not exist!";\
使用这个 makefile 文件,make 命令将会把 sum 安装到目录 /tmp 下(实际上,应用程序一般是安装在 /usr/local/bin 下的,这里为了方便就放到 /tmp 下了) 。执行 make install 命令,将得到如下结果:
输出结果显示 sum 已被成功安装到了 /tmp 目录下(实际上就是把可执行文件 sum 复制到 /tmp 目录下)。再进入 /tmp 目录查看,可以看到可执行文件 sum,其文件权限是 rwxr-xr-x,与 makefile 文件中所设置的一致。
3.3 makefile 文件中的宏
在 makefile 文件中定义一个宏很简单,如下:
MACRONAME=value
这里定义了一个宏 MACRONAME,引用宏的方法是使用 $(MACRONAME) 或 ${MACRONAME} 。使用宏定义,可以让 makefile 文件的可移植性更强。除了自己定义一些宏以外,make 命令还内置了一些特殊的宏定义,使得 makefile 文件变得更加简洁:
当前目标所依赖的文件列表中比当前目标文件还要新的文件
当前目标的名字
当前依赖文件的名字
不包括后缀名的当前依赖文件的名字
除了在 makefile 文件里面定义宏以外,还可以调用 make 命令时,在命令行上给出宏定义。命令行上的宏定义将 覆盖在 makefile 文件中的宏定义。需要注意的是,在 make 命令后接宏定义时,宏定义必须以单个参数的形式传递,因此,需要避免在宏定义中使用空格或加引号。
参考资料:
《Linux 程序设计 第四版》
https://www.ibm.com/support/knowledgecenter/zh/ssw_aix_71/com.ibm.aix.cmds3/make.htm