Makefile編寫規則(三)條件判斷和偽目標


Makefile編寫規則(三)條件判斷和偽目標

Makefile條件判斷

使用 Makefile 編譯文件時,可能會遇到需要分條件執行的情況,比如在一個工程文件中,可編譯的源文件很多,但是它們的類型是不相同的,所以編譯文件使用的編譯器也是不同的。手動編譯去操作文件顯然是不可行的(每個文件編譯時需要注意的事項很多),所以 make 為我們提供了條件判斷來解決這樣的問題。

條件語句可以根據一個變量的值來控制 make 執行或者時忽略 Makefile 的特定部分,條件語句可以是兩個不同的變量或者是常量和變量之間的比較。

注意:條件語句只能用於控制 make 實際執行的 Makefile 文件部分,不能控制規則的 shell 命令執行的過程。下面是條件判斷中使用到的一些關鍵字:

關鍵字 功能
ifeq 判斷參數是否不相等,相等為 true,不相等為 false。
ifneq 判斷參數是否不相等,不相等為 true,相等為 false。
ifdef 判斷是否有值,有值為 true,沒有值為 false。
ifndef 判斷是否有值,沒有值為 true,有值為 false

1)ideq和ifneq

條件判斷的使用方式如下:

ifeq (ARG1, ARG2)
ifeq 'ARG1' 'ARG2'
ifeq "ARG1" "ARG2"
ifeq "ARG1" 'ARG2'
ifeq 'ARG1' "ARG2"

實例:

libs_for_gcc= -lgnu
normal_libs=
foo:$(objects)
ifeq($(CC),gcc)
    $(CC) -o foo $(objects) $(libs_for_gcc)
else
    $(CC) -o foo $(objects) $(noemal_libs)
endif

條件語句中使用到三個關鍵字“ifeq”、“else”、“endif”。其中:“ifeq”表示條件語句的開始,並指定一個比較條件(相等)。括號和關鍵字之間要使用空格分隔,兩個參數之間要使用逗號分隔。參數中的變量引用在進行變量值比較的時候被展開。“ifeq”,后面的是條件滿足的時候執行的,條件不滿足忽略;“else”表示當條件不滿足的時候執行的部分,不是所有的條件語句都要執行此部分;“endif”是判斷語句結束標志,Makefile 中條件判斷的結束都要有。
其實 "ifneq" 和 "ifeq" 的使用方法是完全相同的,只不過是滿足條件后執行的語句正好相反

2) ifdef 和 ifndef

使用方式如下:它的主要功能是判斷變量的值是不是為空。

ifdef VARIABLE-NAME

實例1:

bar =
foo = $(bar)
all:
ifdef foo
    @echo yes
else
    @echo  no
endif

實例2:

foo=
all:
ifdef foo
    @echo yes
else
    @echo  no
endif

通過兩個實例對比說明:通過打印 "yes" 或 "no" 來演示執行的結果。我們執行 make 可以看到實例 1打印的結果是 "yes" ,實例 2打印的結果是 "no" 。其原因就是在實例 1 中,變量“foo”的定義是“foo = $(bar)”。雖然變量“bar”的值為空,但是“ifdef”的判斷結果為真,這種方式判斷顯然是有不行的,因此當我們需要判斷一個變量的值是否為空的時候需要使用“ifeq" 而不是“ifdef”。

Makefile偽目標

偽目標可以這樣來理解,它並不會創建目標文件,只是想去執行這個目標下面的命令。偽目標的存在可以幫助我們找到命令並執行。

使用偽目標有兩點原因:

  • 避免我們的 Makefile 中定義的只執行的命令的目標和工作目錄下的實際文件出現名字沖突。
  • 提高執行 make 時的效率,特別是對於一個大型的工程來說,提高編譯的效率也是我們所必需的。

我們先來看一下第一種情況的使用。如果需要書寫這樣一個規則,規則所定義的命令不是去創建文件,而是通過 make 命令明確指定它來執行一些特定的命令。實例:

clean:
    rm -rf *.o test

規則中 rm 命令不是創建文件 clean 的命令,而是執行刪除任務,刪除當前目錄下的所有的 .o 結尾和文件名為 test 的文件。當工作目錄下不存在以 clean 命令的文件時,在 shell 中輸入 make clean 命令,命令 rm -rf *.o test 總會被執行 ,這也是我們期望的結果。

如果當前目錄下存在文件名為  clean 的文件時情況就會不一樣了,當我們在 shell 中執行命令 make clean,由於這個規則沒有依賴文件,所以目標被認為是最新的而不去執行規則所定義的命令。因此命令 rm 將不會被執行。為了解決這個問題,刪除 clean 文件或者是在 Makefile 中將目標 clean 聲明為偽目標。將一個目標聲明稱偽目標的方法是將它作為特殊的目標.PHONY的依賴,如下:

.PHONY:clean

這樣 clean 就被聲明成一個偽目標,無論當前目錄下是否存在 clean 這個文件,當我們執行 make clean 后 rm 都會被執行。而且當一個目標被聲明為偽目標之后,make 在執行此規則時不會去試圖去查找隱含的關系去創建它。這樣同樣提高了 make 的執行效率,同時也不用擔心目標和文件名重名而使我們的編譯失敗。

在書寫偽目標的時候,需要聲明目標是一個偽目標,之后才是偽目標的規則定義。目標 "clean" 的完整書寫格式如下:

.PHONY:clean
clean:
    rm -rf *.o test

偽目標的另一種使用的場合是在 make 的並行和遞歸執行的過程中,此情況下一般會存在一個變量,定義為所有需要 make 的子目錄。對多個目錄進行 make 的實現,可以在一個規則的命令行中使用 shell 循環來完成。如下:

偽目標的另一種使用的場合是在 make 的並行和遞歸執行的過程中,此情況下一般會存在一個變量,定義為所有需要 make 的子目錄。對多個目錄進行 make 的實現,可以在一個規則的命令行中使用 shell 循環來完成。如下:

SUBDIRS=foo bar baz
subdirs:
    for dir in $(SUBDIRS);do $(MAKE) -C $$dir;done

代碼表達的意思是當前目錄下存在三個子文件目錄,每個子目錄文件都有相對應的 Makefile 文件,代碼中實現的部分是用當前目錄下的 Makefile 控制其它子模塊中的 Makefile 的運行,但是這種實現方法存在以下幾個問題:

  •  當子目錄執行 make 出現錯誤時,make 不會退出。就是說,在對某個目錄執行 make 失敗以后,會繼續對其他的目錄進行 make。在最終執行失敗的情況下,我們很難根據錯誤提示定位出具體實在那個目錄下執行 make 發生的錯誤。這樣給問題定位造成很大的困難。為了解決問題可以在命令部分加入錯誤檢測,在命令執行的錯誤后主動退出。不幸的是如果在執行 make 時使用了 "-k" 選項,此方式將失效。
  • 另外一個問題就是使用這種 shell 循環方式時,沒有用到 make 對目錄的並行處理功能。由於規則的命令是一條完整的 shell 命令,不能被並行處理

有了偽目標之后,我們可以用它來克服以上方式所存在的兩個問題,代碼展示如下:

SUBDIRS=foo bar baz
.PHONY:subdirs $(SUBDIRS)
subdirs:$(SUBDIRS)
$(SUBDIRS):
    $(MAKE) -C $@
foo:baz

上面的實例中有一個沒有命令行的規則“foo:baz”,這個規則是用來規定三個子目錄的編譯順序。因為在規則中 "baz" 的子目錄被當作成了 "foo" 的依賴文件,所以 "baz" 要比 "foo" 子目錄更先執行,最后執行 "bar" 子目錄的編譯

偽目標實現多文件編輯

如果在一個文件里想要同時生成多個可執行文件,我們可以借助偽目標來實現。使用方式如下:

.PHONY:all
all:test1 test2 test3
test1:test1.o
    gcc -o $@ $^
test2:test2.o
    gcc -o $@ $^
test3:test3.o
    gcc -o $@ $^

我們在當前目錄下創建了三個源文件,目的是把這三個源文件編譯成為三個可執行文件。將重建的規則放到 Makefile 中,約定使用 "all" 的偽目標來作為最終目標,它的依賴文件就是要生成的可執行文件。這樣的話只需要一個 make 命令,就會同時生成三個可執行文件。
之所以這樣寫,是因為偽目標的特性,它總會被執行,所以它依賴的三個文件的目標就不如 "all" 這個目標新,所以,其他的三個目標的規則總是被執行,這也就達到了我們一口氣生成多個目標的目的。我們也可以實現單獨的編譯這三個中的任意一個源文件(我們想去重建 test1,我們可以執行命令make test1 來實現 )。 

 


免責聲明!

本站轉載的文章為個人學習借鑒使用,本站對版權不負任何法律責任。如果侵犯了您的隱私權益,請聯系本站郵箱yoyou2525@163.com刪除。



 
粵ICP備18138465號   © 2018-2025 CODEPRJ.COM