最近修改項目代碼的時候發現一個很奇怪的現象,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 版權聲明:本文爲博主原創文章,轉載請附上博文連接!