转:【版权声明:转载请保留出处:blog.csdn.NET/gentleliu。Mail:shallnew at 163 dot com】
1.变量
命令make –p可以打印出很多默认变量和隐含规则。
1.1基础补漏
其实变量的定义有三种运算符=
、:=
、?=
、+=
。
=
:运算符可以读取到后面定义的变量。:=
:运算符在遇到变量定义时立即展开。符合C语言的编程习惯。?=
:运算符在复制之前先做判断变量是否已经存在。例如var1 ?= $(var2)的意思是:
- 如果var1没有定义过,那么
?=
相当于=
- 如果var1先前已经定义了,则什么也不做,不会给var重新赋值。
- 如果var1没有定义过,那么
+=
运算符是给变了追加值。如果变量还没有定义过就直接用+=
赋值,那么+=
相当于=
.
1.2显示
make会把执行的命令打印在屏幕上,如果我们不想把命令打印在屏幕上,只显示命令结果时,直接在命令前面加上符号“@”就可以实现。如上面help目标一样,只显示命令结果。一般我们会在make时都会输出“Compiling xxx.c…”,不输出编译时的命令。
2.嵌套结构
在大一些的项目里面,所有源代码不会只放在同一个目录,一般各个功能模块的源代码都是分开的,各自放在各自目录下,并且头文件和.c
源文件也会有各自的目录,这样便于项目代码的维护。这样我们可以在每个功能模块目录下都写一个Makefile,各自Makefile处理各自功能的编译链接工作,这样我们就不必把所有功能的编译链接都放在同一个Makefile里面,这可使得我们的Makefile变得更加简洁,并且编译的时候可选择编译哪一个模块,这对分块编译有很大的好处。
在每个目录下写一个Makefile,通过最顶层的Makefile一层一层的向下嵌套执行各层Makefile。
$(MAKE) -C src
:就是进入src目录继续执行该目录下的Makefile。
$(MAKE) -C src/ipc
$(MAKE) -C src/tools
$(MAKE) -C src/main
每增加一个目录都要在多个伪目标里面加入一行,这样不够自动化啊,于是我们想到shell的循环语 句,我们可以在每条规则的命令处使用for
2.1shell循环
DIR=src
SUBDIR=$(shell ls $(DIR))
all:
for(subdir in $(SUBDIR));\
do $(MAKE) -C $(DIR)/$$sudir;\
done
2.2改进
上面for循环会依次进入系统命令ls列出的目录,但我们对每个目录的make顺序可能有要求,在该项目当中,main目录下的Makefile必须最后执行,因为最终的链接需要其他目录编译生成的库文件,否则会执行失败。并且在当前的Makefile中,当子目录执行make出现错误时,make不会退出。在最终执行失败的情况下,我们很难根据错误的提示定位出具体是是那个目录下的Makefile出现错误。这给问题定位造成了很大的困难。为了避免这样的问题,在命令执行错误后make退出.所以将刚才的Makefile修改为如下do $(MAKE) -C $(DIR)/$$subdir || exit 1;
3.参数专递
在多个Makefile嵌套调用时,有时我们需要传递一些参数给下一层Makefile。比如我们在顶层Makefile里面定义的打开调试信息变量DEBUG_SYMBOLS
,我们希望在进入子目录执行子Makefile时该变量仍然有效,这是需要将该变量传递给子Makefile,那怎么传递呢?这里有两种方法:
- 1.在上层Makefile中使用
export
关键字对需要传递的变量进行声明。
- 当不希望将一个变量传递给子 make 时,可以使用指示符
unexport
来声明这个变量。
- 当不希望将一个变量传递给子 make 时,可以使用指示符
- 2.在命令行上指定变量。比如:
$(MAKE) -C xxx DEBUG_SYMBOLS = TRUE
3.include 嵌套makefile
Makefile的嵌套调用,每一个模块都有自己的Makefile。其实每个模块的Makefile都大同小异,只需要改改最后编译成生成的目标名称或者编译链接选项,规则都差不多,那么我们是否可以考虑将规则部分提取出来,每个模块只需修改各自变量即可。这样是可行的,我们将规则单独提取出来,写一个Makefile.rule,将他放在顶层Makefile同目录下,其他模块内部的Makefile只需要include该Makefile就可以了。如下:include $(SRC_BASE)/Makefile.rule