make 中的路徑搜索(十二)
那麽下面的 makefile 能夠編譯成功嗎?
我們來試試,編譯結果如下
那麽結果肯定會是這樣的,因為我們在 makefile 中就沒有指定路徑,它在當前目錄下沒有找到源文件。接下來我們就得來介紹一個特殊的預定義變量 VPATH (全大寫),VAPTH 變量的值用於指示 make 如何查找文件,不同文件夾可作為 VPATH 的值同時出現,文件夾的名字之間需要使用分隔符進行區分。
make 對於 VAPTH 值的處理方式:1、當前文件夾找不到需要的文件時,VAPTH 會被使用;2、make 會在 VAPTH 指定的文件夾中依次搜索文件;3、當多個文件夾存在同名文件時,選擇第一次搜索到的文件。註意事項:1、VAPTH 只能決定 make 的搜索路徑,無法決定命令的搜素路徑;2、對於特定的編譯命令(gcc),需要獨立指定編譯搜索路徑。如下
下來我們還是以代碼為例來進行說明,在上面的 makefile 基礎上進行改寫
OBJS := func.o main.o INC := inc SRC := src VPATH := $(INC) $(SRC) hello.out : $(OBJS) @gcc -o $@ $^ @echo "Target File ==> $@" $(OBJS) : %.o : %.c func.h @gcc -o $@ -c $^
我們來編譯試試看
我麽你看到編譯還是報錯,原因就是我們在 gcc 的時候依賴文件中還包含有 func.h,下來我們將最後一行的 $^ 改為 $<。再來試試看
我們在編譯的時候,編譯器又報錯了,說是找不到 func.h。我們的 func.h 文件在 inc 文件中放著,編譯器並不知道去這個文件夾下找,只是在當前文件下進行查找。現在就用到了我們上面說的為特定的編譯命令(gcc)獨立的指定編譯路徑。加上 CFLAGS := -I $(INC),並將 CFLAGS 變量加在 gcc 編譯的命令中。再來看看編譯結果
我們看到已經正確編譯了,並且可執行程序也完美運行。看似問題完美的解決了,但其實 VPATH 也存在一定的問題,當 inc 文件夾中意外出現源文件(c/cpp 文件),那麽可能就會產生編譯錯誤。我們如果將 func.c 誤放在 inc 文件夾中,這個 func.c 文件中打印的是 this is from inc ... ,看看會發生什麽
我們看到輸出的竟然會是 inc 文件中的 func.c 的內容,按照預想應該是 hello makefile 啊。那麽這時的解決方案便是利用 vpath 關鍵字(全小寫),它是為不同類型的文件指定不同的搜索路徑。語法是在 Directory 中搜索符合 Pattern 的規則的格式,格式如下
我們再次用 vpath 來進行設置,看看編譯結果是怎樣的
我們看到已經正確實現了。那麽既然有設置這個規則,是否也可以有取消此規則的設置呢?當然有了。在進行某個具體搜索規則的取消時,是 vpath Pattern(如下圖);取消所有的設置規則時,直接 vpath 即可。
我們來試試,直接在下面加上 vpath %.h,代碼如下
OBJS := func.o main.o INC := inc SRC := src CFLAGS := -I $(INC) vpath %.h $(INC) vpath %.c $(SRC) hello.out : $(OBJS) @gcc -o $@ $^ @echo "Target File ==> $@" vpath %.h $(OBJS) : %.o : %.c func.h @gcc $(CFLAGS) -o $@ -c $<
編譯結果如下
我們看到編譯標錯了,說找不到 func.h 頭文件啦。當然找不到了,我們取消了 .h 頭文件的搜索路徑了。
下來我們來看看如果當 VPATH 和 vpath 同時出現時,make 會如何處理呢?如下
下面的項目中,會選擇哪一個文件夾進行編譯?
我們還是以代碼為例來進行編譯看看
VPATH := src1 CFLAGS := -I inc vpath %.c src2 vpath %.h inc app.out : func.o main.o @gcc -o $@ $^ @echo "Target File ==> $@" $(OBJS) : %.o : %.c func.h @gcc $(CFLAGS) -o $@ -c $<
編譯結果如下
我們看到它是按照 vpath 關鍵字指定的路徑下進行編譯的。我們如果將 src2 文件夾中的 func.c 換成 func.cpp 呢,看看結果
我們看到:make 首先在當前文件夾搜索需要的文件,如果失敗的話,make 優先在 vpath 指定的文件夾中搜索目標文件;當 vpath 搜索失敗時,轉而搜索 VPATH 指定的文件夾。如下
下來我們看看當使用 vpath 對同一個 Pattern 指定多個文件夾時,make 會如何處理?如下
下面的項目中,會選擇哪一個文件夾進行編譯?
我們來編譯看看結果
我們看到編譯的是第一個文件夾 src1 。make 首先在當前文件夾搜索需要的文件,如果失敗:make 以自上而下的順序搜索 vpath 指定的文件夾,當找到目標文件,搜索結束。如下
下面來看看通過 VPATH 指定搜索路徑後,make 如何決定目標文件的最終位置?下面的項目中,會選擇哪一個文件夾進行編譯?
我們來看看編譯結果
我們看到生成可執行程序 app.out。並且成功運行。下來我們將 app.out 放入 src 文件夾中,重新 make,編譯器應該是重新生成一個 app.out
它提示 app.out 是最新的,為什麽沒有重新生成 app.out 呢?仔細分析下,在當前文件夾下沒找到,但是編譯器不死心,繼續在 VPATH 變量指定的路徑下進行尋找,終於在 src 文件夾下找到了 app.out,所以直接就不更新了。那麽我們在 func.c 源文件中改變輸出的字符串為 hello D.T.Software 呢?看看結果
我們看到重新生成了一個 app.out,在 src 文件夾下的 app.out 是沒有改變的。但是在當前目錄下又重新生成了一個 app.out 可執行程序。當 app.out 完全不存在時,make 在當前文件下創建 app.out。當 src 文件夾下存在 app.out 時,所有目標和依賴的新舊關系不變,make 不會重新創建 app.out;當依賴文件被更新,make 在當前文件夾下創建 app.out。
那麽問題就來了,當依賴改變時,如何使得 src 下的 app.out 也被更新呢?解決方案便是使用 GPATH 特殊變量指定目標文件夾;GPATH := src ,當 app.out 完全不存在時,make 默認在當前文件夾創建 app.out;當 app.out 存在於 src,且依賴文件被更新,make 在 src 中創建 app.out。下來還是以代碼為例來進行說明
GPATH := src PATH := src CFLAGS := -I inc app.out : func.o main.o @gcc -o $@ $^ @echo "Target File ==> $@" %.o : %.c inc/func.h @gcc $(CFLAGS) -o $@ -c $<
編譯結果如下
在工程項目中:1、盡量使用 vpath 為不同文件指定搜索路徑;2、不要在源碼文件夾中生成目標文件;3、為編譯得到的結果創建獨立的文件夾;4、避免 VPATH 和 GPATH 特殊變量的使用。 通過對 make 中的路徑搜索的學習,總結如下:1、VPATH 變量用於指示 make 如何查找文件;2、make 會在 VPATH 指定的文件夾中依次搜索文件;3、vpath 關鍵字可以為不同類型的文件指定不同的搜索路徑;4、vpath 比 VPATH 更靈活易用,可動態設置/取消搜索路徑。
歡迎大家一起來學習 makefile,可以加我QQ:243343083。
make 中的路徑搜索(十二)