第六載:makefile變量的高級主題

    在makefile中,我們已經知道變量的基本賦值方法、使用等,但是在makefile中,變量還有一些高級的屬性,掌握這些屬性,可以使編寫的makefile更加高效,現在就來了解下makefile中變量的高級屬性

    一、變量值的替換

        - makefile支持使用指定字符(串)替換變量值中的後綴字符(串)
        - 語法格式爲:$(var:a=b) 或者 ${var:a=b},即使用b替換變量var的後綴a,假如var爲cba,那個替換後的結果cbb
          注意:替換表達式中不能有任何空格, make中也支持使用${}方式對變量進行取值

.PHONY : all

src := a.cpp b.cpp c.cpp
obj := $(src:.cpp=.o)

all:
    @echo "obj => $(obj)"

如上的makefile內容,執行後結果如下(使用.o替換src變量中的.cpp後綴)

/home/delphi>make all
obj => a.o b.o c.o
/home/delphi>

    二、變量的模式替換

        上邊說的變量值的替換,僅僅是使用新的字符串替換變量的指定後綴字符串,而模式替換的替換範圍稍微廣一些,可以保留變量的指定字符,替換其他字符。
        - 使用%保留變量值中的指定字符,替換其他字符(%可以理解爲make中的通配符)
        - 語法格式:$(var:a%b=x%y)或${var:a%b=x%y},即將var變量的前綴後綴a\b分別用x\y替換,中間部分不變。

.PHONY : all

src := hello.cpp hello.cpp hello.cpp
obj := $(src:h%.cpp=H%.o)

all:
    @echo "obj => $(obj)"

    上述的makefile,我們H、.o分別替換src變量的h前綴與.cpp後綴,運行結果如下

/home/delphi>make all
obj => Hello.o Hello.o Hello.o
/home/delphi>

   三、規則中的模式替換

    targets : target-pattern : prereq-pattern
              command1
              command2
              ......
     意義爲:
      -通過target-patterntargets匹配子目標;再通過prereq-pattern子目標生成依賴;進而構成完整的規則。這個稍微不大好理解,下邊用一個例子來說明

對於   $(obj) : %.o : %.c,會對變量obj進行逐個處理,首先拿%.o規則去匹配obj變量,得到main.o,main.o作爲一個子目標,改行表達式就成爲main.o : %.c,再使用%.c生成main.o的依賴,最後衍生成main.o : main.c這樣的目標依賴關係,最終得到$@與$^;同理,遍歷完obj中的main.o後,會接着遍歷fun.o,邏輯與main.o一樣,使用規則中的模式替換的方式可以避免對每個.c文件都寫一條編譯命令,下邊來看一個makefile文件

.PHONY : clean rebuild all


target := hello.out
CC := gcc

obj : main.o func.o

#使用模式規則替換,避免每一條.c都要寫一個編譯命令
$(obj) : %o : %c
        $(CC) -o $@ -c $^

$(target) : main.o func.o
        $(CC) -o $@ $^


all : $(target)

rebuild : clean all

clean : 
        rm -f *.o $(target)

執行結果

/home/delphi/myshare/makefile>make all
gcc    -c -o main.o main.c
gcc    -c -o func.o func.c
gcc -o hello.out main.o func.o
/home/delphi/myshare/makefile>

從結果看出,規則中的模式替換,可以大大簡化我們的工作量,尤其在項目中有成千上百個文件要編譯時。

    四、變量值的嵌套引用

       - 一個變量名之中可以包含對其他變量的引用
       - 嵌套引用的本質是使用一個變量表示另一個變量

.PHONY : all

x := y
y := z
val := $($(x))

all:
    @echo "val => $(val)"

make all,這個嵌套引用比較簡單

/home/delphi>make all
val => z
/home/delphi>

    五、命令行變量

        - 運行make時,在命令行定義變量
        - 命令行變量默認覆蓋makefile中定義的變量
      
  - makefile中使用override關鍵字修飾變量時,命令行變量值不能覆蓋makefile中定義的變量

.PHONY : all test

ifeq ($(DEBUG),true)
    result := this is debug
else
    result := this is release
endif

val_1 := this_is_val_1
override val_2 := this_is_val_2

all:
    @echo "result => $(result)"
    @echo "val_1 => $(val_1)"
    @echo "val_2 => $(val_2)"

執行命令,參數從命令行傳入make all DEBUG:=true val_1:=cmd_this_is_val_1  val_2:=cmd_this_is_val_2

/home/delphi>make all DEBUG:=true val_1:=cmd_this_is_val_1  val_2:=cmd_this_is_val_2
result => this is debug
val_1 => cmd_this_is_val_1
val_2 => this_is_val_2
/home/delphi>

參數從命令行傳入的好處是,我們可以直接在make參數中決定編譯debug版本還是release版本

    六、環境變量(全局變量)

        - makefile中能夠直接使用環境變量的值
        a、makefile中定義與環境變量同名的變量,環境變量會被覆蓋
        b、運行make時指定"- e"選項,優先使用環境變量
        注意:所有makefile都可以直接使用環境變量,但是過多使用環境變量,會降低makefile的移植性

        - 變量在不同makefile之間的傳遞方式
        a、直接在外部定義環境變量進行傳遞(不推薦,可移植性差)
        b、使用export定義變量進行傳遞(定義臨時環境變量)
        c、使用make 命令行變量進行傳遞(推薦)
        爲了說明變量的跨文件傳輸,我們編寫兩個makefile測試:makefile與makefile1

#makefile 內容

.PHONY : all

QTDIR := MY_QTDIR    #QTDIR爲環境變量,此處更改爲MY_QTDIR,會覆蓋原有環境變量,影響makefile1的QTDIR
val1 := this_is_val1
export val2 := this_is_val2    #定義臨時環境變量,makefile1中可直接使用


all:
    @echo "QTDIR => $(QTDIR)"
    @echo "val1 => $(val1)"
    @echo "val2 => $(val2)"

    @echo "---------make test -f makefile1---------"
    @$(MAKE) test -f makefile1    #通過-f 指定make執行makefile1(下同)
    @echo "---------make test -f makefile1 end---------"

    @echo "---------make test -f makefile1 val1:=$(val1)---------"
    @$(MAKE) test -f makefile1 val1:=$(val1)    #通過命令行傳遞變量val1值
    @echo "---------make test -f makefile1 val1:=$(val1) end---------"
#makefile1 內容

.PHONY : test

test:
        @echo "QTDIR => $(QTDIR)"
        @echo "val1 => $(val1)"
        @echo "val2 => $(val2)"

 make all結果如下,可以看到,在makefile更改的QTDIR環境變量會覆蓋原有的系統環境變量;export定義的臨時環境變量val2在makefile1中也可直接訪問;而普通變量在makefile中通過make參數傳遞到makefile1中,才能爲makefile1所用

/home/delphi>make all
QTDIR => MY_QTDIR
val1 => this_is_val1
val2 => this_is_val2
---------make test -f makefile1---------
make[1]: 正在進入目錄 `/home/delphi'
QTDIR => MY_QTDIR
val1 => 
val2 => this_is_val2
make[1]:正在離開目錄 `/home/delphi'
---------make test -f makefile1 end---------
---------make test -f makefile1 val1:=this_is_val1---------
make[1]: 正在進入目錄 `/home/delphi'
QTDIR => MY_QTDIR
val1 => this_is_val1
val2 => this_is_val2
make[1]:正在離開目錄 `/home/delphi'
---------make test -f makefile1 val1:=this_is_val1 end---------
/home/delphi>

     七、目標變量及模式變量(二者均爲局部變量,模式變量時目標變量的擴展)

        - 目標變量:作用域只在指定目標連帶規則中
        - 模式變量:作用域只在符合模式的目標及連帶規則中
       
說的直白點就類似於C語言中函數內部的局部變量,只不過這裏的函數換成的目標
       
下邊直接來看一個例子

.PHONY : all test another

val := this_is_val     #val作用域爲整個makefile文件(同比C語言源文件中的全局變量)

test :  val := this_is_test_val   #val作用域爲test目標或者連帶規則中(同比C語言中函數中的局部變量)
%e : val := this_is_%e_val    #val作用域爲以e結尾的目標或者連帶規則中(同比C語言中函數中的局部變量)

all:
        @echo "all:"
        @echo "val => $(val)"

test : another
        @echo "test:"
        @echo "val => $(val)"

another :
        @echo "another:"
        @echo "val => $(val)"

rule :
        @echo "rule:"
        @echo "val => $(val)"
/home/delphi>
/home/delphi>make all test rule 
all:
val => this_is_val
another:
val => this_is_test_val
test:
val => this_is_test_val
rule:
val => this_is_%e_val
/home/delphi>

從運行結果可以看出,可以給變量限制不同的作用域。

以上7點便是變量的一些高級屬性,需要在工作使用過程中慢慢體會。

以上內容參考《狄泰軟件學院》操作系統篇之 - makefile專題

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