make對命令返回值的檢測

最近修改項目代碼的時候發現一個很奇怪的現象,make在遇到編譯錯誤的時候並無終止,而是正常的生成了目標文件,固然使用這個目標文件時給我帶來了不少痛楚…shell

讓咱們一塊兒來填平這個坑吧,相關Makefile以下:bash

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}/dispatchapp


TARGET  = tpn_audit
OBJS    = tpn_audit.ospa

all: sub $(TARGET).net

$(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執行結果以下:blog

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  版權聲明:本文爲博主原創文章,轉載請附上博文連接!

相關文章
相關標籤/搜索