昨天为了用Makefile来编译一个几十个源码文件的项目,研究了下Makefile的用法,发现Makefile原来是这么强大,一点心得写在这里。
首先发现原来的Makefile写的有些问题,文件中对最终生成的目标的依赖项只设了所有的cpp文件,类似于这样:
这样写虽然很方便,但是有个问题就是当某个cpp文件依赖的头文件有修改后,make不会检测到,这可能会造成一些莫名奇妙的问题。
所以我就按照书上的标准格式,参照每个cpp文件依赖的头文件,手写依赖项……终于写完了,并make成功。事后发现自己真是有愚公移山的精神 -_-!
这样做的缺点很明显,工作量太大,而且一旦代码有改动,makefile也可能需要跟着改,很麻烦。于是我查看了一下Eclipse生成的makefile,发现里面写得像甲古文一样跟本看不懂,但是有一点,其中没有涉及到具体的代码文件,所以一定有更简单的方法。
于是我就到网上找,资料很多,看了不少高手写的功能超强大的Makefile,可惜就是看不懂,所以就东拼西凑,找些尽量简单的来终于完成了一个比较通用又比较简单的Makefile。
(要复制代码的朋友请注意:由于这个编辑器不能输入tab,故用空格代替,代码中缩进的部分都是一个tab;
另外,如果中文注释影响make就删掉)
下面对这个Makefile进行解释:
首先文件中定义了一些依赖于具体项目文件结构的MACRO,如头文件的位置,源文件的位置,编译参数等。其中:
这句的作用是从源文件目录中获得一个所有 cpp文件名的列表
wildcard是make内部定义的函数,能够展开一个正则表达式成为一个字符列列表,空格后面是其仅有的一个参数,代码中表示了SRCDIR目录中的所有cpp文件,故 ${wildcard ${SRCDIR}/*.cpp}能返回cpp文件列表,这样免去了手动输入的麻烦。
是一个所有.o文件名的列表,它利用现成的MACRO SRCS,将其中的.cpp换成.o,SRCS:.cpp=.o就是这个意思。
是一个所有.d文件名的列表,什么是.d文件呢?这是笔者自定义的一个文件,比如main.cpp会有一个对应的main.d,
里面存的是main.cpp中所依赖的头文件,但这个文件是怎么生成的呢,请往下看。
这句定义了从cpp到d的转化规则,定义之后当make需要.d文件的时候,就会把cpp文件按规则自动转换成d文件,它事实上是利用了g++的一个功能 -M,这个参数可以让g++输出一个对应某个cpp文件所依赖的头文件列表,如果要生成main.cpp的依赖列表可以这样:
g++ -M -I includeDir main.cpp
大家可以试一下这个命令,当然这里我们要将输出结果导到.d文件中,所以就构成了如下命令:
g++ -M -I includeDir main.cpp > main.d
Makefile中的命令就是根据这个而来的,只不过用了MACRO代替一些变量,其中:
$<与$@都是make内部定义的MACRO,$<表示当前依赖规则的被依赖项,$@表示目标项,
在.cpp.d:的转换规则定义中.d文件依赖于.cpp,故$<表示main.cpp,$@表示main.d
这样一个完整的依赖关系就能够非常方便的自动建立。
第一篇文章,不容易啊,大家支持一下 : )
首先发现原来的Makefile写的有些问题,文件中对最终生成的目标的依赖项只设了所有的cpp文件,类似于这样:
- all:target_program
- target_program: XXX.cpp ......
- g++ -o target_program XXX.cpp ......
所以我就按照书上的标准格式,参照每个cpp文件依赖的头文件,手写依赖项……终于写完了,并make成功。事后发现自己真是有愚公移山的精神 -_-!
这样做的缺点很明显,工作量太大,而且一旦代码有改动,makefile也可能需要跟着改,很麻烦。于是我查看了一下Eclipse生成的makefile,发现里面写得像甲古文一样跟本看不懂,但是有一点,其中没有涉及到具体的代码文件,所以一定有更简单的方法。
于是我就到网上找,资料很多,看了不少高手写的功能超强大的Makefile,可惜就是看不懂,所以就东拼西凑,找些尽量简单的来终于完成了一个比较通用又比较简单的Makefile。
- all:mserver
- # compiler
- CC = g++
- # include files directory
- INCLUDE = -I include
- # source files directory
- SRCDIR = source
- # lib
- LIBS = -lpthread -lrt
- # options for debug
- CFLAGS = -g
- # options for release
- #CFLAGS = -O
- # get name list of all source file
- SRCS = ${wildcard ${SRCDIR}/*.cpp}
- # get name list of all object file
- OBJS = ${SRCS:.cpp=.o}
- # get name list of all dependency file
- DEPENDS = ${SRCS:.cpp=.d}
- # executable file
- FINAL_PROG = mserver
- all:${FINAL_PROG}
- # 重定义cpp到o的转化规则
- .SUFFIXES: .cpp
- .cpp.o:
- ${CC} -c ${INCLUDE} $< -o $@
- # 定义新的后缀.d,并定义从cpp到d的转化规则
- .SUFFIXES: .d
- .cpp.d:
- ${CC} -M ${INCLUDE} $< > $@
- # 目标文件的依赖关系与生成规则
- ${FINAL_PROG}:${OBJS}
- ${CC} ${CFLAGS} ${LIBS} ${OBJS} -o $@
- # 加入自动生的的所有cpp文件的依赖规则
- -include ${DEPENDS}
- clean:
- -rm -rf ${DEPENDS} ${OBJS}
另外,如果中文注释影响make就删掉)
下面对这个Makefile进行解释:
首先文件中定义了一些依赖于具体项目文件结构的MACRO,如头文件的位置,源文件的位置,编译参数等。其中:
- SRCS = ${wildcard ${SRCDIR}/*.cpp
wildcard是make内部定义的函数,能够展开一个正则表达式成为一个字符列列表,空格后面是其仅有的一个参数,代码中表示了SRCDIR目录中的所有cpp文件,故 ${wildcard ${SRCDIR}/*.cpp}能返回cpp文件列表,这样免去了手动输入的麻烦。
- OBJS = ${SRCS:.cpp=.o}
- DEPENDS = ${SRCS:.cpp=.d}
里面存的是main.cpp中所依赖的头文件,但这个文件是怎么生成的呢,请往下看。
- .SUFFIXES: .d
- .cpp.d:
- ${CC} -M ${INCLUDE} $< > $@
g++ -M -I includeDir main.cpp
大家可以试一下这个命令,当然这里我们要将输出结果导到.d文件中,所以就构成了如下命令:
g++ -M -I includeDir main.cpp > main.d
Makefile中的命令就是根据这个而来的,只不过用了MACRO代替一些变量,其中:
$<与$@都是make内部定义的MACRO,$<表示当前依赖规则的被依赖项,$@表示目标项,
在.cpp.d:的转换规则定义中.d文件依赖于.cpp,故$<表示main.cpp,$@表示main.d
这样一个完整的依赖关系就能够非常方便的自动建立。
第一篇文章,不容易啊,大家支持一下 : )