C語言的本質(38)——makefile之變數
我們詳細看看Makefile中關於變數的語法規則。先看一個簡單的例子:
foo = $(bar)
bar = Huh?
all:
@echo$(foo)
我們執行make將會打出Huh?。當make讀到foo = $(bar)時,確定foo的值是$(bar),但並不立即展開$(bar),然後讀到bar = Huh?,確定bar的值是Huh?,然後在執行規則all:的命令列表時才需要展開$(foo),得到$(bar),再展開$(bar),得到Huh?。因此,雖然bar的定義寫在foo之後,$(foo)展開還是能夠取到$(bar)的值。
這種特性有好處也有壞處。好處是我們可以把變數的值推遲到後面定義,例如:
main.o: main.c
$(CC)$(CFLAGS) $(CPPFLAGS) -c $<
CC = gcc
CFLAGS = -O -g
CPPFLAGS = -Iinclude
編譯命令可以展開成gcc -O -g -Iinclude -cmain.c。通常把CFLAGS定義成一些編譯選項,例如-O、-g等,而把CPPFLAGS定義成一些預處理選項,例如-D、-I等。用=號定義變數的延遲展開特性也有壞處,就是有可能寫出無窮遞迴的定義,例如CFLAGS = $(CFLAGS) -O,或者:
A = $(B)
B = $(A)
當然,make有能力檢測出這樣的錯誤而不會陷入死迴圈。有時候我們希望make在遇到變數定義時立即展開,可以用:=運算子,例如:
x := foo
y := $(x) bar
all:
@echo"-$(y)-"
當make讀到y :=$(x) bar定義時,立即把$(x)展開,使變數y的取值是foo bar,如果把這兩行顛倒過來:
y := $(x) bar
x := foo
那麼當make讀到y :=$(x) bar時,x還沒有定義,展開為空值,所以y的取值是 bar,注意bar前面有個空格。一個變數的定義從=後面的第一個非空白字元開始(從$(x)的$開始),包括後面的所有字元,直到註釋或換行之前結束。如果要定義一個變數的值是一個空格,可以這樣:
nullstring :=
space := $(nullstring) # end ofthe line
nullstring的值為空,space的值是一個空格,後面寫個註釋是為了增加可讀性,如果不寫註釋就換行,則很難看出$(nullstring)後面有個空格。
還有一個比較有用的賦值運算子是?=,例如foo ?= $(bar)的意思是:如果foo沒有定義過,那麼?=相當於=,定義foo的值是$(bar),但不立即展開;如果先前已經定義了foo,則什麼也不做,不會給foo重新賦值。
+=運算子可以給變數追加值,例如:
objects = main.o
objects += $(foo)
foo = foo.o bar.o
object是用=定義的,+=仍然保持=的特性,objects的值是main.o$(foo)(注意$(foo)前面自動添一個空格),但不立即展開,等到後面需要展開$(objects)時會展開成main.o foo.o bar.o。
再比如:
objects := main.o
objects += $(foo)
foo = foo.o bar.o
object是用:=定義的,+=保持:=的特性,objects的值是main.o$(foo),立即展開得到main.o (這時foo還沒定義),注意main.o後面的空格仍保留。
如果變數還沒有定義過就直接用+=賦值,那麼+=相當於=。
上一節我們用到了特殊變數$@和$<,這兩個變數的特點是不需要給它們賦值,在不同的上下文中它們自動取不同的值。常用的特殊變數有:
• $@,表示規則中的目標。
• $<,表示規則中的第一個條件。
• $?,表示規則中所有比目標新的條件,組成一個列表,以空格分隔。
• $^,表示規則中的所有條件,組成一個列表,以空格分隔。
例如前面寫過的這條規則:
main: main.o stack.o maze.o
gccmain.o stack.o maze.o -o main
可以改寫成:
main: main.o stack.o maze.o
gcc$^ -o $@
這樣即使以後又往條件裡新增了新的目標檔案,編譯命令也不需要修改,減少了出錯的可能。
$?變數也很有用,有時候希望只對更新過的條件進行操作,例如有一個庫檔案libsome.a依賴於幾個目標檔案:
libsome.a: foo.o bar.o lose.owin.o
arr libsome.a $?
ranliblibsome.a
這樣,只有更新過的目標檔案才需要重新打包到libsome.a中,沒更新過的目標檔案原本已經在libsome.a中了,不必重新打包。
在上一節我們看到make的隱含規則資料庫中用到了很多變數,有些變數沒有定義(例如CFLAGS),有些變數定義了預設值(例如CC),我們寫Makefile時可以重新定義這些變數的值,也可以在預設值的基礎上追加。以下列舉一些常用的變數,請讀者體會其中的規律。
AR
靜態庫打包命令的名字,預設值是ar。
ARFLAGS
靜態庫打包命令的選項,預設值是rv。
AS
彙編器的名字,預設值是as。
ASFLAGS
彙編器的選項,沒有定義。
CC
C編譯器的名字,預設值是cc。
CFLAGS
C編譯器的選項,沒有定義。
CXX
C++編譯器的名字,預設值是g++。
CXXFLAGS
C++編譯器的選項,沒有定義。
CPP
C前處理器的名字,預設值是$(CC) -E。
CPPFLAGS
C前處理器的選項,沒有定義。
LD
連結器的名字,預設值是ld。
LDFLAGS
連結器的選項,沒有定義。
TARGET_ARCH
和目標平臺相關的命令列選項,沒有定義。
OUTPUT_OPTION
輸出的命令列選項,預設值是-o $@。
LINK.o
把.o檔案連結在一起的命令列,預設值是$(CC) $(LDFLAGS) $(TARGET_ARCH)。
LINK.c
把.c檔案連結在一起的命令列,預設值是$(CC) $(CFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)。
LINK.cc
把.cc檔案(C++原始檔)連結在一起的命令列,預設值是$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(LDFLAGS) $(TARGET_ARCH)。
COMPILE.c
編譯.c檔案的命令列,預設值是$(CC) $(CFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c。
COMPILE.cc
編譯.cc檔案的命令列,預設值是$(CXX) $(CXXFLAGS) $(CPPFLAGS) $(TARGET_ARCH) -c。
RM
刪除命令的名字,預設值是rm -f。
常用的make命令列選項:
-n選項只列印要執行的命令,而不會真的執行命令,這個選項有助於我們檢查Makefile寫得是否正確,由於Makefile不是順序執行的,用這個選項可以先看看命令的執行順序,確認無誤了再真正執行命令。
-C選項可以切換到另一個目錄執行那個目錄下的Makefile,比如先退到上一級目錄再執行我們的Makefile(假設我們的原始碼都放在testmake目錄下):
$ cd ..
$ make -C testmake
make: Entering directory`/home/djkings/testmake'
cc -c -o main.o main.c
cc -c -o stack.o stack.c
cc -c -o maze.o maze.c
gcc main.o stack.o maze.o -o main
make: Leaving directory`/home/djkings/testmake'
一些規模較大的專案會把不同的模組或子系統的原始碼放在不同的子目錄中,然後在每個子目錄下都寫一個該目錄的Makefile,然後在一個總的Makefile中用make -C命令執行每個子目錄下的Makefile。例如Linux核心原始碼根目錄下有Makefile,子目錄fs、net等也有各自的Makefile,二級子目錄fs/ramfs、net/ipv4等也有各自的Makefile。
在make命令列也可以用=或:=定義變數,如果這次編譯我想加除錯選項-g,但我不想每次編譯都加-g選項,可以在命令列定義CFLAGS變數,而不必修改Makefile編譯完了再改回來:
$ make CFLAGS=-g
cc -g -c -o main.o main.c
cc -g -c -o stack.o stack.c
cc -g -c -o maze.o maze.c
gcc main.o stack.o maze.o -o main
如果在Makefile中也定義了CFLAGS變數,則命令列的值覆蓋Makefile中的值。
相關文章
- C語言的本質(36)——makefile基礎C語言
- C語言的本質(37)——makefile之隱含規則和模式規則C語言模式
- C語言的本質(32)——C語言與彙編之C語言內聯彙編C語言
- C語言的本質(26)——C標準庫之數值字串轉換C語言字串
- C語言的本質(22)——C標準庫之字串操作C語言字串
- C語言的本質(35)——共享庫C語言
- C語言的本質(34)——靜態庫C語言
- C語言學習筆記之變數C語言筆記變數
- 理解函數語言程式設計的本質函數程式設計
- C語言的本質(24)——C標準庫之輸入與輸出(下)C語言
- 新建一個最小的Makefile工程(C語言)C語言
- C 語言輸出100至200之間的質數(素數)
- C語言sizeof()變數、字元、字串C語言變數字元字串
- C語言多維陣列本質技術推演C語言陣列
- Go語言slice的本質-SliceHeaderGoHeader
- 【Makefile】5-Makefile變數的基礎變數
- C語言可變引數詳解C語言
- C語言裡全域性變數管理C語言變數
- C語言的本質(19)——預處理之一:巨集定義C語言
- 程式語言:型別系統的本質型別
- C語言-變數常量資料型別C語言變數資料型別
- C語言--靜態區域性變數C語言變數
- C語言檢視變數位元組程式C語言變數
- 一分鐘開始持續整合之旅系列之:C 語言 + Makefile
- 試題 演算法提高 質數2(C語言)演算法C語言
- [ASM C/C++] C語言函式的可選性自變數ASMC++C語言函式變數
- 聊聊C語言和指標的本質C語言指標
- go語言變數Go變數
- C語言“字串-數字”之間的轉換C語言字串
- C語言怎麼實現可變引數C語言
- C語言指標(二) 指標變數 ----by xhxhC語言指標變數
- C 語言的變數作用域及標頭檔案變數
- C語言putenv()函式:改變或增加環境變數C語言函式變數
- 澄清Java語言介面與繼承的本質(轉)Java繼承
- 【c語言】將正數變成對應的負數,將負數變成對應的正數C語言
- GO語言————4.4 變數Go變數
- c語言 - 交換兩個變數(不建立臨時變數)兩種方法C語言變數
- makefile--變數的應用(下)變數