一个简单的示例项目有如下几个部分
1,makefile文件
2,src的目录,src目录中存在app.c,app.h,main.c,lib.c和bar.c几个文件
若使用手工编译则
gcc -o appexp src/main.c src/app.c src/bar.c src/lib.c
若使用makefile文件,则可以写一个最简单的makefile文件
第一种 最简单makefile文件
appexp:main.o app.o bar.o lib.o
gcc -o appexp main.o app.o bar.o lib.o
main.o:src/main.c src/lib.h src/app.h
gcc -c -o main.o src/main.c
app.o:src/app.c src/lib.h src/app.h
gcc -c -o app.o src/app.c
bar.o:src/bar.c src/lib.h
gcc -c -o bar.o src/bar.c
lib.o:src/lib.c src/lib.h
gcc -c -o lib.o src/lib.c
以上是最简单的makefile文件
makefile文件包括规则 和命令两部分形式如下
规则
(tap)命令
其中规则分为两部分 目标:依赖关系
如上例中 appexp:main.o app.o bar.o lib.o 此规则表明 目标为appexp,生成该目标所依赖的的文件为 main.o app.o bar.o lib.o
完成该规则所需要的命令为 gcc -o appexp main.o app.o bar.o lib.o
make会依次去查询每一个.o文件,如果全部存在且不存在时间上的错位则执行该命令,否则进一步去查找不存在或者需要重新生成的.o文件的规则。
make会把这些规则链接到一起,最后生成一个依赖树。make只对修改过的依赖进行重新生成。
make 并不管命令是如何工作的,他只管执行所定义的命令, make会比较目标文件和依赖文件的修改日期,若依赖文件的修改日期比目标文件要新,或者目标文件不存在,则make就会重新生成目标文件。所以若main.o app.o bar.o lib.o中 lib.c修改过,则会重新生成lib.o,并且重新生成appexp,但是未做修改的其余三个.o文件不进行重新生成。
----------------------------------------------------------------------------------------------------------------------------------------------------------
若工程较为简单,则使用以上的makefile可以解决问题,但是当工程较为负责的时候,相互依赖关系写起来就很麻烦,所以在makefile文件中可以使用一些函数或者通配符来进行比对
第二种是使用模式匹配规则的makefile文件
也是经常见的一种如下
SRC_FILES = main.c app.c bar.c lib.c
OBJ_FILES = $(patsubst %.c , %.o, ${SRC_FILES})
VPATH = src
CFLAGS = -c -g
LDFLAGS = -g
appexp:${OBJ_FILES}
gcc ${LDFLAGS} -o appexp &{OBJ_FILES}
%.o:%.c
gcc &{CFLAGS} -o $@ $
clean :
rm *o appexp
MAIN_HDRS = lib.h app.h
LIB_HDRS = lib.h
main.o : $(addprefix src/,${MAIN_HDRS})
app.o : $(addprefix src/, ${MAIN_HDRS})
bar.o : $(addprefix src/, ${LIB_HDRS})
lib.o : $(addprefix src/,${LIB_HDRS})
这种方法利用了模式匹配,第一个SRC_FILES 变量保存着 所有的.c文件 第二行中的OBJ_FILES 变量中使用了patsubst函数将所有的.c文件转换为同名的.o文件,
并且保存在OBJ_FILES 中。
其实仔细看也会发现 makefile文件还是严格按照规则来的。
目标为appexp 依赖关系为 ${OBJ_FILES} 无非是使用了变量,这里可以将OBJ_FILES想想成是c语言中的宏替换,在使用时替换为其表示的字符串,不过不同于宏的是他是可以进行一些函数操作的。
紧接着的命令语句 gcc ${LDFLAGS} -o appexp &{OBJ_FILES} 如果直接翻译就翻译成 gcc -g -o appexp main.o app.o bar.p lib.o 其实和最简单的一种makefile是相同的。
%.o:%.c
gcc &{CFLAGS} -o $@ $
这个就比较有意思,这个也是完全符合makefile规则的,%.o是目标 %.c是依赖文件 然后 gcc &{CFLAGS} -o $@ $ 是生成目标的命令。
这里用了通配符,解释之后其实也就是类似于main.o:main.c .......等等 和第一种makefile写法也是一致的。
在命令中 使用到了$@ 和$ 其中$@代表规则中:(冒号)左边的文件名,$代表:(冒号)右边的文件名,规则可以看成是 $@ : $ 的形式,这样命令也相似的解释成了第一种版本的情况。
最后的MAIN_HDRS 和LIB_HDRS声明了头文件,并且最后说明了四个.o文件对头文件的依赖关系 。
main.o : $(addprefix src/,${MAIN_HDRS}) 翻译出来就是 main.o 依赖于src/lib.h 和src/app.h
这种makefile文件的扩展性比第一种要强,但是还是需要手动确定头文件依赖关系。还有一种makefile写法可以对依赖关系进行自动跟踪。
-----------------------------------------------------------------------------------------------------------------------------------------------
第三种,使用依赖跟踪的makefile
SRC_FILES = main.c app.c bar.c lib.c
OBJ_FILES = $(patsubst %.c , %.o, ${SRC_FILES})
DEP_FILES = $(patsubst %.c , %.dep, ${SRC_FILES})
VPATH = src
CFLAGS = -c -g
LDFLAGS = -g
appexp:${OBJ_FILES}
gcc ${LDFLAGS} -o appexp &{OBJ_FILES}
%.o:%.c
gcc &{CFLAGS} -o $@ $
clean :
rm *o appexp
include ${DEP_FILES}
%.dep:%.c
@set -e ;rm -f $@;\
gcc -MM $(CFLAGS) $<> $@.$$$$ ; \
sed 's,\($*\)\.o[ :]*,\1.o $@ :,g' < $@.$$$$ > $@ ; \
rm -f $@.$$$$
和第二种差不多,只是多了一个DEP_FILES 用来记录相应的DEP文件,.dep文件用于保存为相应源文件自动生成的依赖规则。
include ${DEP_FILES} 建立了makefile和.dep文件之间的依赖关系,当该makefile执行时,他会先找到所需要的.dep文件,并且必须等所有的.dep文件都找到了才进行make的执行。
下面这一段将.c文件生成.dep文件,这点还没弄太明白.
%.dep:%.c
@set -e ;rm -f $@;\ #先清理掉.dep文件
gcc -MM $(CFLAGS) $<> $@.$$$$ ; \ # 使用c编译器产生基本的#include依赖规则
sed 's,\($*\)\.o[ :]*,\1.o $@ :,g' < $@.$$$$ > $@ ; \ #使用sed命令处理上一行的输出,使.dep文件依赖于相应源文件的依赖,当文件改变时,.dep也重新生成
rm -f $@.$$$$
上文生成的dep文件其实和手动写的是一样的
比如说生成的main.dep文件的内容如下:
main.o main.dep : src/main.c src/lib.c src/app.h
以上的方法大多适用于较小的项目,因为对大项目来说,有automake和autoconf工具,可以比较方便的自动生成makefile文件。不过要读懂它 还是要学会这些基本的知识。
-------------内容部分参考自《GUN/LINUX环境编程》