Makefile 入門

一:基本概念

  • 目標(target): 位於‘:’的前面,其名字可以是由字母和下劃線‘_’組成。
  • 假目標(phony target):用於解決所定義的目標與所存在的文件是同名的的問題。實例:.PHONY: clean
  • 先決條件(prerequisites):生成某個目標時,所依賴的目標。通常位於':'後面。
  •  命令(commands):通常位於目標下面。每條規則中的命令和操作系統 Shell 的命令行是一致的。make 會按順序一條一條地執行命令,每條命令的開頭必須以[Tab]鍵開頭,除非,命令是緊跟在依賴規則後面的分號後的。在命令行之間中的空格或是空行會被忽略,但是,如果該空格或空行是以 Tab 鍵開頭的,那麼 make 會認爲其是一個空命令。
  •  規則
    • 由目標(targets)、先決條件(prerequisites)以及命令(commands)所組成的。
    • Makefile的基本單元。

            目標和先決條件之間表達的就是依賴關係(dependency),這種依賴關係指明在構建目標之前,必須保證先決條件先滿足(或構建)。而先決條件可以是其它的目標,當先決條件是目標時,其必須先被構建出來。 還有就是一個規則中目標可以有多個,當存在多個目標,且這一規則是 Makefile 中的第一個規則時,如果我們運行 make 命令不帶任何目標,那麼規則中的第一個目標將被視爲是缺省目標。

這裏寫圖片描述

  • 實例
源文件:
//foo.c
#include <stdio.h>
void foo ()
{
    printf (“This is foo ()!\n”);
}

//main.c
extern void foo ();
int main ()
{
    foo ();
    return 0;
}


makefile初版1
#-c:表示只編譯不鏈接。
all: main.o foo.o
    gcc -o simple main.o foo.o
main.o: main.c
    gcc -o main.o -c main.c
foo.o: foo.c
    gcc -o foo.o -c foo.c
clean:
    rm simple.exe main.o foo.o

二:變量

  • 概念:一個變量的定義很簡單,就是一個名字(變量名)後面跟上一個等號,然後在等號的後面放這個變量所期望的值。對            於變量的引用,則需要採用$(變量名)或者${變量名}這種模式。
  •  變量的分類

      1. 自動變量:用於解決目標和先決條件的名字會在規則的命令中多次出現的問題。

$@:用於表示一個規則中的目標。當我們的一個規則中有多個目標時, $@所指的是其中任何造成命令被運行的目標。

$^:則表示的是規則中的所有先擇條件。

$<:表示的是規則中的第一個先決條件。

       2. 特殊變量:make程序定義好的變量。

CC = cc #c語言編譯器的名稱
CPP = $(cc) -E #c文件預處理器的名稱
CFLAGS #C文件的編譯選項
CPPFLAGS #C文件預處理的編譯選項
CXXFLAGS #CPP文件的編譯選項
LDFLAGS #連接的動態庫
 
CURDIR := /home/zxy/... #當前路徑
MAKEFLAGS = p #make命令選項
RM = rm -f
VPATH #文件的搜索路徑

MAKE:指make命令名是什麼。當我們需要在 Makefile 中調用另一個 Makefile 時需要用到這個變量, 採用這種方式,有利於寫一個容易移植的 Makefile。
MAKECMDGOALS: 指的是用戶輸入的目標。
  • 變量的類別(賦值)

      1. 遞歸擴展變量(recursively expanded variable):使用“=”操作符進行變量定義和賦值,遞歸擴展變量的引用是遞歸的。                 其值爲推導到最後一次的賦值值。
      2. 簡單擴展變量(simply expanded variables):使用“:=”操作符來定義的。 對於這種變量, make 只對其進行一次掃描和                  替換。
      3. 條件賦值符:使用“?=”操作符來定義的。條件賦值的意思是當變量以前沒有定義時,就定義它並且將左邊的值賦值給
              它,如果已經定義了那麼就不再改變其值。條件賦值類似於提供了給變量賦缺省值的功能。
      4. 追加賦值:使用“+=”操作符來定義的。原變量用追加一個新值。

      5. 多行變量

define two-lines
echo foo
echo $(bar)
endef

 

  • 變量及其值的來源

        1 . 對於前面所說到的自動變量,其值是在每一個規則中根據規則的上下文自動獲得變量值的。
        2 .可以在運行 make 時,在 make 命令行上定義一個或多個變量。如果採用 make bar=x 運行 Makefile,則得到的結果將完               全不同。在 make 命令行中定義的變量及其值同樣在 Makefile 中是可見的。其實,我們可以通過在 make 命令行中定義              變量的方式從而覆蓋 Makefile 中所定義的變量的值。
        3 .變量還可以來自於 Shell 環境, 如,採用 Shell 中的 export 命令定義了一個 bar變量。

  • 高級變量引用功能

       在斌值的同時完成後綴替換操作。如:

PHONY: all
foo = a.o b.o c.o
bar := $(foo:.o=.c)
all:
    @echo "bar = $(bar)"

 

  • override 指令

       我們可以採用在 make 命令行上定義變量的方式, 使得 Makefile 中定義的變量被覆蓋掉,從而不起作用。 可能,在設計             Makefile 時,我們並不希望用戶將我們在 Makefile 中定義的某個變量覆蓋掉,那就得用 override 指令了。

#更新makefile2:

.PHONY: clean
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o

$(EXE): $(OBJS)
    $(CC) -o $@ $^
main.o: main.c
    $(CC) -o $@ -c $^
foo.o: foo.c
    $(CC) -o $@ -c $^
clean:
    $(RM) $(EXE) $(OBJS)

三:模式

  •  解決問題:

        如果對於每一個目標文件都得寫一個不同的規則來描述,那會是一種“體力活”,太繁了!對於一個大型項目, 就更不用說了。 Makefile 中的模式就是用來解決我們的這種煩惱的。
 

# 更新makefile3:

.PHONY: clean
CC = gcc
RM = rm
EXE = simple
OBJS = main.o foo.o

$(EXE): $(OBJS)
    $(CC) -o $@ $^
    
#模式應用
%.o: %.c
    $(CC) -o $@ -c $^
    
clean:
    $(RM) $(EXE) $(OBJS)

四:條件判斷

      使用條件判斷,可以讓make根據運行時的不同情況選擇不同的執行分支。條件表達式可以是比較變量的值,或是比較變量和常量的值。

注意:
    make是在讀取Makefile時就計算條件表達式的值,並根據條件表達式的值來選擇語句,所以,你最好不要把自動化變量(如“$@”等)放入條件表達式中,因爲自動化變量是在運行時纔有的。

條件表達式的語法爲:
    # 111    
    <conditional-directive>
    <text-if-true>
    endif

    # 222
    <conditional-directive>
    <text-if-true>
    else
    <text-if-false>
    endif

    # 333
    ifeq (<arg1>, <arg2>)
    <text-if-true>
    else ifeq (<arg3>, <arg4>)
    <text-if-true>
    else
    <text-if-true>
    endif

其中<conditional-directive>表示條件關鍵字,如“ifeq”。這個關鍵字有四個。
(1)第一個是“ifeq”:
    注:ifeq 後面參數要加$(), 因爲是值引用, 值可以爲數值或字符串
    ifeq (<arg1>, <arg2> ) 
比較參數“arg1”和“arg2”的值是否相同。當然,參數中還可以使用make的函數。如:
    ifeq ($(strip $(foo)),)
    <text-if-empty>
    endif
這個示例中使用了“strip”函數,如果這個函數的返回值是空(Empty),那麼<text-if-empty>就生效。

(2)第二個條件關鍵字是“ifneq”。語法是:
    ifneq (<arg1>, <arg2> ) 
其比較參數“arg1”和“arg2”的值是否相同,如果不同,則爲真。和“ifeq”類似。

(3)第三個條件關鍵字是“ifdef”。語法是:
     ifdef <variable-name> 
    如果變量<variable-name>是否有值,那表達式爲真。否則,表達式爲假。當然,<variable-name>同
樣可以是一個函數的返回值。注意,ifdef只是測試一個變量是否有值,其並不會把變量擴展到當前位置。還是來看兩個例子:
    注:ifdef後的變量名不能加 $()。
示例一:
    bar =
    foo = $(bar)
    ifdef foo
    frobozz = yes
    else
    frobozz = no
    endif
示例二:
    foo =
    ifdef foo
    frobozz = yes
    else
    frobozz = no
    endif
第一個例子中,“$(frobozz)”值是“yes”,第二個則是“no”。

(4)第四個條件關鍵字是“ifndef”。其語法是:
     ifndef <variable-name>
 和“ifdef”是相反的意思。
     在<conditional-directive>這一行上,多餘的空格是被允許的,但是不能以[Tab]鍵做爲開始(不然
就被認爲是命令)。而註釋符“#”同樣也是安全的。“else”和“endif”也一樣,只要不是以[Tab]鍵開始就行了。



feq ($(TARGET_ARCH), arm)
        LOCAL_SRC_FILES := ...
else ifeq ($(TARGET_ARCH), x86)
        LOCAL_SRC_FILES := ...
else ifeq ($(TARGET_ARCH), mips)
        LOCAL_SRC_FILES := ...
else 
        LOCAL_SRC_FILES := ...
endif


五:函數

  • addprefix 函數

addprefix 函數是用來在給字符串中的每個子串前加上一個前綴,其形式是:
        $(addprefix prefix, names...)
實例代碼:

.PHONY: all
without_dir = foo.c bar.c main.o
with_dir := $( addprefix objs/, $(without_dir))
all:
    @echo $(with_dir)

 

  •  filter函數

 filter 函數用於從一個字符串中,根據模式得到滿足模式的字符串,其形式是:
       $(filter pattern..., text)
實例代碼:

.PHONY: all
sources = foo.c bar.c baz.s ugh.h
sources := $(filter %.c %.s, $(sources))
all:
    @echo $(sources)
  • filter-out函數

 filter-out 函數用於從一個字符串中根據模式濾除一部分字符串,filter 與 filter-out 是互補的。其形式是:
      $(filter-out pattern..., text)
實例代碼:

.PHONY: all
objects = main1.o foo.o main2.o bar.o
result = $(filter-out main%.o, $(objects))
all:
    @echo $(result)

 

  •  patsubst函數

 patsubst 函數是用來進行字符串替換的,其形式是:
     $(patsubst pattern, replacement, text)
實例代碼:

.PHONY: all
mixed = foo.c bar.c main.o
objects := $(patsubst %.c, %.o, $(mixed))
all:
    @echo $(objects)
  •  strip函數

 strip 函數用於去除變量中的多餘的空格,其形式是:
      $(strip string)
實例代碼:

.PHONY: all
original = foo.c bar.c
stripped := $(strip $(original))
all:
    @echo "original = $(original)"
    @echo "stripped = $(stripped)"
  •  wildcard 函數

 wildcard 是通配符函數,通過它可以得到我們所需的文件,這個函數如果我們在 Windows 或是Linux 命令行中的“*”。 其形式是:
     $(wildcard pattern)
實例代碼:

.PHONY: all
SRCS = $(wildcard *.c)
all:
    @echo $(SRCS)
# 更新makefile4 

.PHONY: clean
CC = gcc
RM = rm
EXE = simple
SRCS = $(wildcard *.c)
OBJS = $(patsubst %.c,%.o,$(SRCS))
$(EXE): $(OBJS)
    $(CC) -o $@ $^
%.o: %.c
    $(CC) -o $@ -c $^
clean:
    $(RM) $(EXE) $(OBJS)

 

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