1. 概述
Makefile,what??很多Windows程序可能都没听说过
简单的说,Makefile是Unix/Linux环境下描述了整个工程的编译、连接等规则的文件,其主要包括三点:
1) 工程中的哪些源文件需要编译以及如何编译
2) 依赖库以及库所在的位置
3) 想得到什么:可执行文件?静态库?动态库?
项目中,我们会有很多源文件、头文件、依赖库文件、配置文件等等,通过Makefile定义规则来制定编译顺序,编译规则,编译依赖,甚至更复杂的功能,将极大的方便我们的开发,其最大的好处就是”自动化编译“,通过‘make’就可以方便的进行整个项目的编译工作。
2. 编译链接
从源码到可执行文件,具体步骤:
源码--->预处理--->编译--->汇编--->链接
我们常常把预处理、编译和汇编三个阶段统称为编译阶段,在这个阶段,编译器会检查程序语法、函数与变量是否声明等。
经过编译之后,unix/linux下,将得到.o文件(一般来说,每个源文件都能生成一个对应的.o文件),即object file(windows下即.obj文件),.o不能直接运行,我们需要将其合成可执行文件,这个过程就叫链接。在链接过程,链接器会在所有的.o文件中找寻函数的实现,如果找不到,则会报链接错误。
3.Makefile规则
Makefile只有一个规则:
target: prerequisites
command
target:即目标,它可以是可执行文件、可以是.o文件,也可以是一个标签,简单的说,它就是你要做的事情。
prerequisites:生成target所需要的条件,它可以是一个文件,也可以是另外一个target
command:具体执行的命令
解释如下:target这个目标依赖于prerequisites中的文件,其生成规则定义在command中。
更简单一点表达:如果prerequisites中任何一个文件的时间要比target文件更新的话,command所定义的命令就会执行。
eg:
=====makefile=====
#第一个规则
test: main.o hello.o
gcc main.o hello.o –o test
#第二个规则
main.o: main.c
gcc –c main.c
#第三个规则
hello.o: hello.c hello.h
gcc –c hello.c
#第四个规则
clean :
rm –rf *.o
rm –rf test
=====makefile=====
第一个规则:
test就是target, main.o和hello.o是prerequisites,'gcc main.o hello.o –o test’是command。
即:要生成test,就需要有main.o和hello.o,如果main.o或者hello.o文件的时间比test新(或者test文件不存在),则会执行命令’gcc main.o hello.o –o test’,
第二个规则:
target是main.o,main.c是prerequisites,’gcc –c main.c’是command
即:要生成test,就需要main.c,如果main.c文件的时间比main.o新,则会执行’gcc –c main.c’
第三个规则和第二个规则类似
第四个规则:
这里,target是clean,此处,clean并不是一个文件,而是一个动作的名字,它的执行,需要显示的在make命令后制定,例如,此处执行‘make clean’,将调用其后的command,即‘rm –rf *.o rm –rf test’,另外,此处没有prerequisites,即任何时候都执行command
我们来看下如果执行'make'会做什么:
1、make会在当前目录下找名字叫“Makefile”或“makefile”的文件。
2、如果找到,它会找文件中的第一个目标文件(target),在上面的例子中,他会找到“test”这个文件,并把这个文件作为最终的目标文件。
3、如果test文件不存在,或是test所依赖的后面的[.o]文件的文件修改时间要比test这个文件新,那么,他就会执行后面所定义的命令来生成test这个文件。
4、如果test所依赖的.o文件存在,那么make会在当前文件中找目标为.o文件的依赖性,如果找到则再根据那一个规则生成.o文件。
5、依次类推,直到生成的所有的target都是最新的。
用户如果执行'make clean',由于其后面没有依赖,则执行command所定义的命令。
如果我们修改了代码main.c,然后执行‘make’,由于main.c时间比main.o新,那么main.o会重新编译生成,由于main.o文件的时间比test新,那么test也会重新生成。
4. 一个好用的Makefile模板
######################################################################
# makefile 模版
#
######################################################################
###################项目路径和程序名称#################################
DIR=$(shell pwd)
BIN_DIR=$(DIR)/bin
LIB_DIR=$(DIR)/lib
SRC_DIR=$(DIR)/src
INCLUDE_DIR=$(DIR)/include
OBJ_DIR=$(DIR)/obj
DEPS_DIR=$(DIR)/deps
#PROGRAM=$(BIN_DIR)/test
PROGRAM=$(BIN_DIR)/test
###################OBJ文件及路径############################################
EXTENSION=cpp
OBJS=$(patsubst $(SRC_DIR)/%.$(EXTENSION), $(OBJ_DIR)/%.o,$(wildcard $(SRC_DIR)/*.$(EXTENSION)))
DEPS=$(patsubst $(OBJ_DIR)/%.o, $(DEPS_DIR)/%.d, $(OBJS))
###################include头文件路径##################################
INCLUDE=\
-I$(INCLUDE_DIR)
###################lib文件及路径######################################
###################编译选项及编译器###################################
CC=g++
CFLAGS=-Wall -W -g
LDFLAGS=
###################编译目标###########################################
.PHONY: all clean rebuild
all:$(OBJS)
@echo $(DEPS_DIR)
$(CC) -o $(PROGRAM) $(OBJS) $(LDFLAGS)
$(DEPS_DIR)/%.d: $(SRC_DIR)/%.$(EXTENSION)
$(CC) -MM $(INCLUDE) $(CFLAGS) {1}lt; | sed -e 1's,^,$(OBJ_DIR)/,' > $@
sinclude $(DEPS)
$(OBJ_DIR)/%.o:$(SRC_DIR)/%.$(EXTENSION)
$(CC) {1}lt; -o $@ -c $(CFLAGS) $(INCLUDE)
rebuild: clean all
clean:
rm -rf $(OBJS) $(PROGRAM)
工作目录结构:
bin deps include lib Makefile obj src
5. 结语
以上只是Makefile的冰山一角,有兴趣的同学可以看下make的info pages。
Makefile可以很方便的维护中小型项目,但是,在项目大到一定程度,存在众多依赖之后,人力维护Makefile也是一件极为困难的事情。目前有一些自动化的工具,例如Automake,就是一个极好的工具。