>>>>>>>#DON"T MODIFY THE BELOWS
#combine output folder
FinalOutput := $(Output)/$(Compile_Flag)/
上⾯的代码,定义了⼀个变量,名字是FinalOutput,给它赋值,可以⽤=或者:=,等⼀下说区别。
$(Output)表⽰取变量Output的值,在这⾥,Output是bin,所以$(Output)就是bin啦。同理,#(Compile_Flag)就是Debug,组合在⼀起,就是bin/Debug/,把它赋值给变量FinalOutput,现在FinalOutput就是bin/Debug/了。
接下来说=和:=的区别。
=就是把右边的值赋给左边。但是,⽐如下⾯的赋值就会出问题
FinalOutput=$(FinalOutput)
为什么呢?
因为右边的$(FinalOutput)会取FinalOutput的值,这个取值的过程叫做“展开”,有点类似宏的意思。于是,这个展开就会陷⼊⽆穷的递归⾥⾯去了。虽然make很智能,遇到这类问题,它会抱错,但是我们怎么避免呢?于是就有了:=,它的意思就是只展开⼀次。这样就不会陷⼊⽆穷的递归⾥⾯去了。
加油站英文
#list all dirs
SUBDIRS := $(shell find $(SRC) -type d)
这⾏的作⽤,是调⽤shell,执⾏find命令,然后把返回的结果放到变量SUBDIRS⾥⾯。在makefile⾥⾯调⽤shell执⾏命令的⽅法是:$(shell text)
其中,text是要执⾏的命令,⽐如上⾯的find $(SRC) -type d(按照前⾯的设置,这个命令展开后,应该是find code -type d。这个是基本的find命令,意思是查找code⽂件夹⾥⾯的所有⽂件夹,包括code⽂件夹。),命令执⾏的结果,也就是$(shell text)的返回值。
#flags in makefile
DEBUG_FLAG = -O0 -g3 -Wall -c -fmessage-length=0
RELEASE_FLAG = -O3 -Wall -c -fmessage-length=0
这⾥是设置编译器的编译参数,具体内容请参看g++的⼿册吧。如果有不满意的,可以在这⾥修改编译的参数。
RM := rm -rf
这个是清除命令,⽤在clean⾥⾯的
#t compile flag
ifeq ($(Compile_Flag),Debug)
小孩惊厥
CFLAGS := $(DEBUG_FLAG)
el
CFLAGS := $(RELEASE_FLAG)
endif
这⾥是⼀个条件判断。ifeq就是“如果等于”的意思。还有⼀个ifneq,当然就是“如果不等于”了。⽤法很简单,就是
ifeq(要⽐较的内容,要⽐较的内容)
el
endif
上⾯的代码,⽐较了Compile_Flag变量和Debug,如果Compile_Flag的值是Debug,就给变量CFLAGS设置为DEBUG_FLAG的值;如果不是,就设置为RELEASE_FLAG的值。
#prepare files
CPP_SRCS:=$(shell find ./$(SRC) -name *.cpp)
这⾥⼜调⽤了⼀次shell,执⾏了find。这次是查找.cpp⽂件,然后把⽂件列表保存在CPP_SRCS⾥⾯。找到的⽂件是带着相对的路径名的,这个很有⽤。
OBJS:=$(CPP_SRCS:%.cpp=$(FinalOutput)%.o)
这⾥是替换,$(CPP_SRCS:%.cpp=$(FinalOutput)%.o)
的意思就是说,把CPP_SRCS⾥⾯,每个cpp部分结尾的字符串,.cpp部分都替换成.o,并且在前⾯加上$(FinalOutput)字符串。在这个例⼦⾥⾯,$(FinalOutput)就是bin/Debug/。
举个例⼦吧。⽐如你在路径./code/test下⾯有个⽂件a.cpp,执⾏上⾯的操作后,CPP_SRCS⾥⾯就有了⼀个./code/test/a.cpp,然后经过替换,OBJS⾥⾯就有了⼀个bin/Debug/code/test/a.o了。
#all target
all:dir $(FinalOutput)$(PRJ_NAME)
all是我们要make的⽬标,冒号(:)后⾯的内容是这个⽬标的依赖项,依赖项可以没有,也可以有多个。
这个all,就依赖于两个项⽬,⼀个是dir,⼀个是$(FinalOutput)和$(PRJ_NAME)⼀起组成的⽬标⽂件夹。其中dir是⽤来创建⽬录的,⽽$(FinalOutput)$(PRJ_NAME)是⽤来⽣成项⽬⽂件的。
make执⾏的时候,遇到all,要⽣成它,就得先找到它依赖的这两个项⽬。
dir:
mkdir -p $(FinalOutput);
for val in $(SUBDIRS);do \
桃花花苞
mkdir -p $(FinalOutput)$${val}; \
done;
这个dir,没有依赖的项⽬,它要做的事情,就是创建⽂件夹。每个⽬标要执⾏的操作,都要以tab开头,回车结束。如果⼀⾏太长,可以⽤\加回车在下⼀⾏继续。
在这⾥,这两⾏的作⽤,都是建⽴⽂件夹。
mkdir是shell⾥⾯建⽴⽂件夹的命令,-p参数表⽰如果⽂件夹存在,就忽略。
#tool invocations
$(FinalOutput)$(PRJ_NAME):$(OBJS)
@echo 'Building target:
@echo 'Invoking:GCC C++ Linker'
$(PRJ_TYPE) $(LIBPATH) -o"$@" $^ $(LIBS)
古新世@echo 'Finished building target:
@echo ' '
在上⾯的命令⾥⾯,@echo 'Building target:
表⽰显⽰⼀⾏字符串,显⽰内容就是''⾥⾯的。在echo前⾯加个@,意思就是告诉make,在执⾏的时候,不显⽰这⾏命令。如果没有前⾯的@,那么执⾏的结果会像下⾯这样:
echo ''
<
如果前⾯有@,结果会像下⾯这样:
<
被执⾏的命令就不会显⽰出来了。
@echo 'Building target: 这⾏⾥⾯的$@是个特殊的系统变量,它代表“⽬标”,在这⾥就是$(FinalOutput)$(PRJ_NAME)。假如
$(FinalOutput)$(PRJ_NAME)的值是testlib.so,那么$@就会是testlib.so了。
@echo 'Invoking:GCC C++ Linker'这⾏就是简单的显⽰些信息,没什么可说的。
$(PRJ_TYPE) $(LIBPATH) -o"$@" $^ $(LIBS)
这⾏才是真正⼲活的部分。前⾯准备了那么多,⼜说了那么多废话,其实真正⼲活的才刚刚遇到。
其中$@我就不再解释了。$^代表依赖项,也就是$(OBJS)
这⾥的变量展开后,⼤概会如下形式:
g++ -shared -o"bin/Debug/libtest.so" ./code/test/a.o ./code/test/b.o -lpthread
现在编译器会遇到问题。它要⽣成libtest.so⽂件,需要上⾯的两个.o⽂件,可是我们还没有编译出来啊,怎么办?不⽤担⼼,下⾯就要编译.o⽂件了。
这⾥是makefile最核⼼的地⽅,模式规则。
$(FinalOutput)%o:./%cpp
@echo 'Building file: $<'
@echo 'Invoking:GCC C++ Compiler'
g++ $(CFLAGS) $(INCLUDEPATH) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$(@:%.o=%.d)" -o"$@" "$<"
@echo 'Finished building: $<'
@echo ' '
在上⾯的规则⾥⾯,我来说下$(FinalOutput)%o:./%cpp的意思。我还是举例说吧,直接讲不容易搞明⽩。
⽐如我有⼀个⽬标是bin/Debug/src/data/core.o,其中变量$(FinalOutput)的值是bin/Debug/
应⽤规则后对应的依赖⽂件就是./src/data/core.cpp了。
还不明⽩?那我说得再详细⼀些。
⽐如我们建⽴了⼀个项⽬,在⽂件夹testMake下⾯,它下⾯有⽂件夹src,这⾥⾯是代码,还有⽂件夹bin,作为make的输出⽬录,另外就是还有这个makefile⽂件。
在⽬录testMake下⾯执⾏make,会先在bin⽬录下⾯建⽴⽂件夹Debug(如果你配置为relea,那么建⽴的就是Relea)。然后会在
bin/Debug下⾯创建最终⽬标,⽐如是testMake吧。⽽testMake需要当前⽬录下⾯的⽂件bin/Debug/src/data/core.o,这个.o⽂件⼜依赖于当前⽬录下的⽂件./src/data/core.cpp。于是make就会去编译相应的cpp⽂件,把⽣成的.o⽂件放到bin/Debug下⾯对应的位置,然后再link,就⽣成了最终⽬标。
结合上⾯的讲解,在⽣成最终⽬标之前,遇到的每个.o⽂件,都会按照上⾯的规则,⾸先把需要的所有的.o⽂件都编译出来,然后再进⾏链接,⽣成最终的⽬标⽂件。
#other targets
clean:
-$(RM) $(Output)/*
' '
.PHONY:all clean
这⾥是配置的clean的内容,执⾏make clean的时候,会做这些事情。
好了。makefile的学习总结就写到这⾥
最后是makefile的下载地址:
参考资料:
eclip⾃动⽣成的makefile霸气十足的句子
makefile官⽅⼿册