make对命令返回值的检测

最近修改项目代码的时候发现一个很奇怪的现象,make在遇到编译错误的时候并没有终止,而是正常的生成了目标文件,当然使用这个目标文件时给我带来了很多痛楚…

让我们一起来填平这个坑吧,相关Makefile如下:

SUB_SRC += ${TPN_AUDIT_BASE}/protocols/feixin
SUB_SRC += ${TPN_AUDIT_BASE}/protocols/ftp
SUB_SRC += ${TPN_AUDIT_BASE}/protocols/telnet
SUB_SRC += ${TPN_AUDIT_BASE}/protocols/http
SUB_SRC += ${TPN_AUDIT_BASE}/dispatch


TARGET  = tpn_audit
OBJS    = tpn_audit.o

all: sub $(TARGET)

$(TARGET) : $(OBJS)
    $(CC) -o $@ $^ $(LIBS) $(LIBRARY)
    @rm -f $(OBJS)

sub:
    @for SRC in $(SUB_SRC); do cd $${SRC} && make -w ; echo $$? && echo ""; done    #echo ""的作用是将两条连续的打印用空行隔开
    @echo $$?       #后来我调试添加的

clean:
    @for SRC in $(SUB_SRC); do cd $${SRC} && make clean && echo ""; done
    @rm -f $(OBJS) $(TARGET)
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
16
17
18
19
20
21
22
23
make执行结果如下:

make[2]: Entering directory `/scratchbox/test/lidonghai/NJFresh/app/audit/protocols/http`
cc -g -Wall -DADV_TIME -DHTTP_APPLE_MATCH -DLOCAS_REDIRECT ...#中间忽略了部分无关紧要的输出
'http.c:20: error: expected specifier-qualifier-list before 'typedef'
make[2]: *** [http.o] Error 1
make[2]: Leaving directory '`/scratchbox/test/lidonghai/NJFresh/app/audit/protocols/http
make[2]: Entering directory `/scratchbox/test/lidonghai/NJFresh/app/audit/dispatch
....#中间忽略了部分无关紧要的输出
make[2]: Leaving directory `/scratchbox/test/lidonghai/NJFresh/app/audit/dispatch`

0   #这个0很重要的噢!!!它就是我添加的调试语句@echo $$?打印的结果

....#中间忽略了部分无关紧要的输出
make[1]: Leaving directory scratchbox/test/lidonghai/NJFresh/app/audit
root@darkstar:/scratchbox/test/lidonghai/NJFresh# ls -l  app/audit/tpn_audit 
-rwxr-xr-x 1 root root 56133 2016-05-13 19:21 app/audit/tpn_audit*  #虽然有编译错误但目标文件还是生成了...
1
2
3
4
5
6
7
8
9
10
11
12
13
14
15
我的第一反应是make使用了-k参数,但是在这个Makefile里并没有-k呀,难道是上层Makefile传递过来的。我为此还专门研究了下make的递归调用问题,总结了一些相关知识。这个参数一般不会用直接用到项目Makefile中的,而且我确实没有在上层的Makefile文件中搜到该选项。 
那是什么原因导致的呢… 
于是我又确认了下make对命令及相关返回值的处理:

- 规则中,当目标需要被重建时。此规则所定义的命令将会被执行,如果是多行命令,那么每一行命令将在一个独立的子 shell 进程中被执行。就是说,每一行命令的执行是在一个独立的 shell 进城中完成。因此多行命令之间的执行是相互独立的多个进程,相互之间不存在依赖。

- 当make在执行命令时,如果某一条命令执行失败(被一个信号中止,或非零退出),且该条命令产生的错误未被忽略,那么其它的用于重建同一目标的命令执行也将会被终止,make进程以错误告终。

“命令出错make进程退出”,这和我记忆中的相符呀。但是为什么make没退出呢,为什么呢…… 
我试着在make -w的后面和命令行的下一行分别添加echo $$?来显示命令的返回值。然后再编译我发现,make -C http\时返回值为2, 其它都是返回0。 
然后我就猜想,难道是http后面的模块的成功把http的失败给掩盖过去了,从而造成make的错觉? 根据此猜想我把http放到最后,然后再次编译,发现结果和预期一致,make以失败告终目标文件未生成。这好像说明了什么…:)

我又仔细想了下,make怎么可能那么容易被忽悠了。肯定是我哪里理解有误,哪里有误呢? 
make并不是对每个子make -C 的返回值做检测,而是对整个子sh的返回值做检测。而我错误的理解成make会对每个子make -C的返回值做检测。其实这样说也不对,应该是make调用了子进程sh,然后sh又调用了子进程make运行make -C,即make –> sh –> make的关系,所以make只能看到sh的返回值,而sh中最后一条语句的返回值会作为sh的返回值,所以才会出现“把http的编译放到前面,有编译错误但make看不见”的现象。

看来最根本的原因是我没搞清楚进程之间的关系啊:<
知道了原因之后就好解决问题了,应该对 
@for SRC in $(SUB_SRC); do cd $${SRC} && make -w ; echo $$? && echo ""; done中的每个make -w 的返回值进行检测,一旦有错误就向make返回错误,从而可以保证整个构建结果的正确性。

正确的修改如下:

sub:
    @for dir in $(SUB_SRC); do \
        $(MAKE) -C $${dir};\
        if [ "$$?" != "0" ]; then\
            exit 1;\
        fi;\
    done
1
2
3
4
5
6
7
这样就会在子模块出错的时候立即被make发现并终止整个构建过程。

总结一下:
1. make会针对每行命令创建一个子sh进程来运行相应的命令;
2. make会对每个子sh进程的返回值做检测,如果返回错误make就会终止运行;
3. 对于子sh中运行的各种子进程make是不关心的,make只关心它们的领导sh进程反馈的结果;
4.显示Makefile变量使用$前缀,显示bash变量使用$$前缀。
--------------------- 
作者:lidonghat 
来源:CSDN 
原文:https://blog.csdn.net/lidonghat/article/details/51397994 
版权声明:本文为博主原创文章,转载请附上博文链接!

转载于:https://my.oschina.net/u/4000302/blog/3037547

  • 0
    点赞
  • 1
    收藏
    觉得还不错? 一键收藏
  • 0
    评论
评论
添加红包

请填写红包祝福语或标题

红包个数最小为10个

红包金额最低5元

当前余额3.43前往充值 >
需支付:10.00
成就一亿技术人!
领取后你会自动成为博主和红包主的粉丝 规则
hope_wisdom
发出的红包
实付
使用余额支付
点击重新获取
扫码支付
钱包余额 0

抵扣说明:

1.余额是钱包充值的虚拟货币,按照1:1的比例进行支付金额的抵扣。
2.余额无法直接购买下载,可以购买VIP、付费专栏及课程。

余额充值