一、几点细节帮助理解Makefile
1. makefile中的变量引用方法为$(VAR), 若为$VAR,则make只对$V进行替换。(我的系统上是这样。)
2. 若要在makefile中写shell指令,引用shell中的变量时的格式是$$VAR。make将$$VAR做一次转椅,扔给shell时,shell看到的是$VAR,符合shell中引用变量的格式。
3. make的规则中的命令若没有;结尾,则每条指令都是在一个新的shell中,若想多条命令在一个shell中执行,可以用;将他们连起来。(涉及到shell执行的方式)
4. make中存在大量隐式规则,简化makefile的编写。隐式规则可以被覆盖。
5. make按照从上到下从左到右读入makefile中的内容,但是在处理makefile中的语句是并不是这样的顺序。
6. make处理makefile分为两个阶段,第一个阶段读入makefile,以及包含的其他makefile,分析变量名及变量值,隐式规则和显式规则
构建所有目标关系树,以及他们的先决条件。
第二阶段根据第一阶段建立的关系树分析哪些目标需要重新构建,执行哪些命令来构建。
7. make处理makefile时存在立即展开(第一阶段就展开)和延迟展开(第二阶段展开)。
变量赋值时,赋值号左边总是立即展开,但是右边的变量却不一定。
所有条件语句都是立即展开,自动变量都是延迟展开(他们都是在规则的命令块中被设置),所以自动变量在条件语句中不能使用。如果一定要使用,则只能使用shell条件语法。
8. 规则的展开策略:目标和依赖都是立即展开,命令只能延迟展开(自动变量在命令中)。
二、举例
项目huge的目录结构如图所示:
1. build目录下的Makefile的作用:分别进入./code/huge/src和./code/foo/src并执行子目录里面的makefile。
2. 将两个子目录的Makefile的公用部分抽离到build下的make.rule。并在子目录中包含make.rule简化子目录的Makefile编写,增加复用性。
1 .PHONY: all clean 2 3 # get the root of the project, you can use func: abspath 4 ROOT=$(realpath ..) 5 6 # list all directory we need to go to. 7 DIRS = $(ROOT)/code/foo/src 8 $(ROOT)/code/bar/src 9 $(ROOT)/code/huge/src 10 11 RM=rm 12 RMFLAFGS=-rf 13 RMS=$(ROOT)/build/bins $(ROOT)/build/libs 14 15 all clean: 16 #tell shell that if any error occur, exit immediately. then make will stop. 17 @set -e; 18 for dir in $(DIRS); 19 do 20 cd $$dir && $(MAKE) ROOT=$(ROOT) -r [email protected]; 21 done 22 @set -e; 23 if [ "$(MAKECMDGOALS)" == "clean" ]; then $(RM) $(RMFLAFGS) $(RMS); fi 24 @echo "" 25 @echo ":-) Completed" 26 @echo ""
./build/Makefile
1 .PHONY: all clean 2 3 MKDIR=mkdir 4 RM=rm 5 RMFLAGS=-rf 6 7 CC=gcc 8 AR=ar 9 ARFLAGS=crs 10 11 DIR_OBJS=objs 12 DIR_BINS=$(ROOT)/build/bins 13 DIR_DEPS=deps 14 DIR_LIBS=$(ROOT)/build/libs 15 DIRS=$(DIR_OBJS) $(DIR_BINS) $(DIR_DEPS) $(DIR_LIBS) 16 RMS=$(DIR_OBJS) $(DIR_DEPS) 17 18 ifneq ("$(BIN)", "") 19 BIN:=$(addprefix $(DIR_BINS)/, $(BIN)) 20 RMS+=$(BIN) 21 endif 22 23 ifneq ("$(LIB)", "") 24 LIB:=$(addprefix $(DIR_LIBS)/, $(LIB)) 25 RMS+=$(LIB) 26 endif 27 28 SRC=$(wildcard *.c) 29 OBJ=$(SRC:.c=.o) 30 OBJ:=$(addprefix $(DIR_OBJS)/, $(OBJ)) 31 DEP=$(SRC:.c=.dep) 32 DEP:=$(addprefix $(DIR_DEPS)/, $(DEP)) 33 34 ifeq ("$(wildcard $(DIR_OBJS))", "") 35 DEP_DIR_OBJS := $(DIR_OBJS) 36 endif 37 38 ifeq ("$(wildcard $(DIR_BINS))", "") 39 DEP_DIR_BINS := $(DIR_BINS) 40 endif 41 42 ifeq ("$(wildcard $(DIR_DEPS))", "") 43 DEP_DIR_DEPS := $(DIR_DEPS) 44 endif 45 46 ifeq ("$(wildcard $(DIR_LIBS))", "") 47 DEP_DIR_LIBS := $(DIR_LIBS) 48 endif 49 50 ifneq ($(INCLUDE_DIRS), "") 51 INCLUDE_DIRS:=$(strip $(INCLUDE_DIRS)) 52 INCLUDE_DIRS:=$(addprefix -I, $(INCLUDE_DIRS)) 53 endif 54 55 ifneq ($(LINK_LIBS), "") 56 LINK_LIBS:=$(strip $(LINK_LIBS)) 57 LIB_ALL:=$(notdir $(wildcard $(DIR_LIBS)/*)) #find out all the filename in "$(DIR_LIBS)/" 58 #TEST_LIB:=$(addprefix $(DIR_LIBS)/, $(LIB_ALL)) 59 LIB_FILTERED:=$(addsuffix .%, $(addprefix lib, $(LINK_LIBS))) # result is libxxx.% 60 $(eval DEP_LIBS=$(filter $(LIB_FILTERED), $(LIB_ALL))) 61 DEP_LIBS:=$(addprefix $(DIR_LIBS)/, $(DEP_LIBS)) 62 LINK_LIBS:=$(addprefix -l, $(LINK_LIBS)) 63 endif 64 65 #first goal must allocate this position 66 all: $(BIN) $(LIB) 67 68 #this include directive will import rule, if "all: $(BIN) $(LIB)" after this include directive 69 #the default target will not be "all: $(BIN) $(LIB)" 70 ifneq ("$(MAKECMDGOALS)", "clean") 71 include $(DEP) 72 endif 73 74 $(DIRS): 75 $(MKDIR) [email protected] 76 77 $(BIN): $(DEP_DIR_BINS) $(OBJ) $(DEP_LIBS) 78 $(CC) -o [email protected] -L$(DIR_LIBS) $(filter %.o, $^) $(LINK_LIBS) 79 80 $(LIB): $(DEP_DIR_LIBS) $(OBJ) 81 $(AR) $(ARFLAGS) [email protected] $(filter %.o, $^) 82 83 $(DIR_OBJS)/%.o: $(DEP_DIR_OBJS) %.c 84 $(CC) -c -o [email protected] $(INCLUDE_DIRS) $(filter %.c, $^) 85 86 $(DIR_DEPS)/%.dep: $(DEP_DIR_DEPS) %.c 87 @echo "Creating [email protected] ..." 88 @set -e; 89 $(RM) $(RMFLAGS) [email protected]; 90 $(CC) -E -MM $(INCLUDE_DIRS) $(filter %.c, $^) > [email protected]; 91 sed ‘s,\(.*\)\.o[ :]*,objs/\1.o [email protected]: ,g‘ < [email protected] > [email protected]; 92 $(RM) $(RMFLAGS) [email protected] 93 94 clean: 95 $(RM) $(RMFLAGS) $(RMS) 96
./build/make.rule
1 BIN= 2 3 LIB=libfoo.a 4 5 INCLUDE_DIRS=$(ROOT)/code/foo/inc 6 7 LINK_LIBS= 8 9 include $(ROOT)/build/make.rule
./code/foo/src/Makefile
1 BIN=huge 2 3 LIB= 4 5 INCLUDE_DIRS=$(ROOT)/code/foo/inc $(ROOT)/code/bar/inc 6 LINK_LIBS=foo bar 7 8 include $(ROOT)/build/make.rule
./code/huge/src/Makefile
三、其他
1. make提供很多命令选项,其中--debug用来打印调试信息,对于理解make的具体执行过程很有帮助。
2. 调试makefile时除了--debug外,还可以通过echo打印。
3. make默认是会从隐式规则中开始查找,如果我们不想要使用隐式规则,使用-r,提高编译效率。
4. 活用make:
经常遇到项目在不同计算机间拷贝或通过ftp传输时,不同计算机的时间设置不同造成项目文件的时间戳是将来的时间。
解决:在makefile添加touch目标,命令是:find $(ROOT) -exec touch {} \;
-exec 参数后面跟的是command命令,它的终止是以;为结束标志的,所以这句命令后面的分号是不可缺少的,考虑到各个系统中分号会有不同的意义,所以前面加反斜杠。