【Linux + Makefile】Makefile的高階用法:解決C文件包含的頭文件修改了,但C文件不重新編譯的問題

在上一篇 《【Linux + Makefile】十分鐘教你學會Makefile的FORCE 》文章的最後,筆者就FORCE的用法在一個示例工程中使用,提出了一個問題:爲何build_info.h每次都是新生成的(有修改過),而main.c又是有include “build-info.h”,但main.c卻不是每次都重新編譯呢?這個到底是不是違反了Makefile的基本規則呢?本文將給你答案,通過閱讀本文,你將瞭解到以下內容:

  • 如何保證在C文件中包含的頭文件修改了的時候,C文件每次都會被重新編譯?

爲了更好地展示上訴描述的問題,我們將之前的示例工程稍微複雜化一點點:

整個工程有3個.c文件,a.c/b.c/main.c,其中main.c會調用a.c/b.c中的兩個接口,同時main.c會include頭文件build_info.h;這個build_info.h每次編譯都會重新生成,按照我們之前的寫法,我們Makefile可能就是這樣:

SHELL           = /bin/bash #指定shell使用/bin/bash,否則echo -e可能會出問題
ECHO            = echo
BIN             = test
BUILG_INFO_H    = build_info.h
SRC-C-y         += a.c
SRC-C-y         += b.c
SRC-C-y         += main.c
SRC-O           = $(patsubst %.c, $(O)%.o, $(SRC-C-y))

all: gen_build_info $(BIN)

clean: 
    rm -rf $(SRC-O) $(BIN) $(BUILG_INFO_H)

$(BIN) : $(SRC-O)
    gcc -o $(O)"$@" $(SRC-O)
	
%.o : %.c
    gcc -c "$<" -o "$@"
	
gen_build_info: $(BUILG_INFO_H)

$(BUILG_INFO_H): FORCE     #強制生成build_info.h
    @$(RM) $@
    @$(ECHO) '  GEN     $@'
    @$(ECHO) -e   " #ifndef __BUILD_INFO_H__\n"\
				"#define __BUILD_INFO_H__\n"\
				"#define APP_TIME        	\"`date "+%Y-%m-%d %H:%M:%S"`\"\n"\
				"#endif"  > $@

FORCE:
.PHONY: FORCE

執行make,我們會發現,跟我們的預期不一樣:它雖然會每次都生成build_info.h,但是main.c包含了build_info.h卻不會每次都重新編譯。這個問題發生的原因,我們來分析下:

在我們的Makefile規則中,main.o只依賴於main.c (Makefile 第18-19行),而在第二次執行make的時候,main.c顯然並沒有被修改,所以main.o不會重新生成,自然可執行文件就不會重新生成。這裏的問題根源在於,main.c它是依賴於build_info.h的,而這個依賴關係並沒有體現在Makefile中,所以整個編譯流程達不到我們的預期想法。我們嘗試下,將main.c的依賴頭文件也寫入到Makefile中,怎麼實現呢?

恰好,GCC給了我們強大的支持,它有個非常有用的選項 -MD -MF,它可以在生成一個.o的同時也生成它的依賴文件列表,修改後的Makefile如下所示:

SHELL           = /bin/bash #指定shell使用/bin/bash,否則echo -e可能會出問題
ECHO            = echo
BIN             = test
BUILG_INFO_H    = build_info.h
SRC-C-y         += a.c
SRC-C-y         += b.c
SRC-C-y         += main.c
SRC-O           = $(patsubst %.c, $(O)%.o, $(SRC-C-y))
SRC-C-DEPS      = $(patsubst %.c, $(O).%.o.d, $(SRC-C-y))  ## 由 a.c ==> .a.o.d

all: gen_build_info $(BIN)

clean: 
    rm -rf $(SRC-O) $(BIN) $(BUILG_INFO_H) $(SRC-C-DEPS)

$(BIN) : $(SRC-O)
    gcc -o $(O)"$@" $(SRC-O)
	
%.o : %.c
#	生成xxx.o的時候,同時生成它的依賴列表,放在文件.xxx.o.d中
	gcc -c "$<" -o "$@" -MD -MF "$(dir $@).$(notdir $@).d" -MT "$@"
	
gen_build_info: $(BUILG_INFO_H)

$(BUILG_INFO_H): FORCE     #強制生成build_info.h
    @$(RM) $@
    @$(ECHO) '  GEN     $@'
    @$(ECHO) -e   " #ifndef __BUILD_INFO_H__\n"\
				"#define __BUILD_INFO_H__\n"\
				"#define APP_TIME        	\"`date "+%Y-%m-%d %H:%M:%S"`\"\n"\
				"#endif"  > $@

FORCE:
.PHONY: FORCE

# 在Makefile末尾強制包含這些依賴文件
-include $(SRC-C-DEPS)

測試結果如下所示:

再次執行make,多試幾次,一樣的結果。

由上可知,經過改造後的Makefile是實現了我們的需求,每次build_info.h重新生成,導致main.c包含了build_info.h也會重新編譯,而a.c和b.c沒有被修改,所以在未執行make clean的情況下,a.c和b.c是不會被重新編譯的,每次都是僅僅main.c被再次編譯,從而重新生成新的test可執行文件。這樣就是已經達到了【當C文件包含的頭文件修改了的時候,C文件必須重新編譯】的目的。


以上就是關於Makefile的高階用法,基本滿足了我們日常工程實踐的需求。如果你對該Makefile有疑問,歡迎在評論席提出你的疑問,博主很樂意爲你解答。

版權申明:本文爲博主原創文章,轉載請註明出處!https://blog.csdn.net/szullc/article/details/85038875

原創作者:李路昌

電子郵箱:[email protected]


延伸閱讀:

【Linux + Makefile】十分鐘教你學會Makefile的FORCE

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章