[Makefile] Makefile 及其工做原理

轉自:https://www.linuxidc.com/Linux/2018-09/154071.htmlinux

 

當你須要在一些源文件改變後運行或更新一個任務時,一般會用到 make 工具。make 工具須要讀取一個 Makefile(或 makefile)文件,在該文件中定義了一系列須要執行的任務。你可使用 make 來將源代碼編譯爲可執行程序。大部分開源項目會使用 make 來實現最終的二進制文件的編譯,而後使用 make install 命令來執行安裝。編程

本文將經過一些基礎和進階的示例來展現 make 和 Makefile 的使用方法。在開始前,請確保你的系統中安裝了 make編程語言

 

基礎示例

依然從打印 「Hello World」 開始。首先建立一個名字爲 myproject 的目錄,目錄下新建 Makefile 文件,文件內容爲:函數

  1. say_hello:
  2.         echo"Hello World"

在 myproject 目錄下執行 make,會有以下輸出:工具

  1. make
  2. echo"Hello World"
  3. HelloWorld

在上面的例子中,「say_hello」 相似於其餘編程語言中的函數名。這被稱之爲目標target。在該目標以後的是預置條件或依賴。爲了簡單起見,咱們在這個示例中沒有定義預置條件。echo ‘Hello World' 命令被稱爲步驟recipe。這些步驟基於預置條件來實現目標。目標、預置條件和步驟共同構成一個規則。spa

總結一下,一個典型的規則的語法爲:code

  1. 目標:預置條件
  2. <TAB>步驟

做爲示例,目標能夠是一個基於預置條件(源代碼)的二進制文件。另外一方面,預置條件也能夠是依賴其餘預置條件的目標。htm

  1. final_target: sub_target final_target.c
  2.         Recipe_to_create_final_target
  3.        
  4. sub_target: sub_target.c
  5.         Recipe_to_create_sub_target

目標並不要求是一個文件,也能夠只是步驟的名字,就如咱們的例子中同樣。咱們稱之爲「僞目標」。遞歸

再回到上面的示例中,當 make 被執行時,整條指令 echo "Hello World" 都被顯示出來,以後纔是真正的執行結果。若是不但願指令自己被打印處理,須要在 echo 前添加 @

say_hello:
        @echo "Hello World"
ip

從新運行 make,將會只有以下輸出:

  1. make
  2. HelloWorld

接下來在 Makefile 中添加以下僞目標:generate 和 clean

  1. say_hello:
  2.         @echo"Hello World"
  3. generate:
  4.         @echo"Creating empty text files..."
  5.         touchfile-{1..10}.txt
  6. clean:
  7.         @echo"Cleaning up..."
  8.         rm*.txt

隨後當咱們運行 make 時,只有 say_hello 這個目標被執行。這是由於Makefile 中的第一個目標爲默認目標。一般狀況下會調用默認目標,這就是你在大多數項目中看到 all 做爲第一個目標而出現。all 負責來調用它他的目標。咱們能夠經過 .DEFAULT_GOAL 這個特殊的僞目標來覆蓋掉默認的行爲。

在 Makefile 文件開頭增長 .DEFAULT_GOAL

  1. .DEFAULT_GOAL := generate

make 會將 generate 做爲默認目標:

  1. make
  2. Creatingempty text files...
  3. touchfile-{1..10}.txt

顧名思義,.DEFAULT_GOAL 僞目標僅能定義一個目標。這就是爲何不少 Makefile 會包括 all 這個目標,這樣能夠調用多個目標。

下面刪除掉 .DEFAULT_GOAL,增長 all 目標:

  1. all: say_hello generate
  2. say_hello:
  3.         @echo"Hello World"
  4. generate:
  5.         @echo"Creating empty text files..."
  6.         touchfile-{1..10}.txt
  7. clean:
  8.         @echo"Cleaning up..."
  9.         rm*.txt

運行以前,咱們再增長一些特殊的僞目標。.PHONY 用來定義這些不是文件的目標。make 會默認調用這些僞目標下的步驟,而不去檢查文件名是否存在或最後修改日期。完整的 Makefile 以下:

  1. .PHONY: all say_hello generate clean
  2. all: say_hello generate
  3. say_hello:
  4.         @echo"Hello World"
  5. generate:
  6.         @echo"Creating empty text files..."
  7.         touchfile-{1..10}.txt
  8. clean:
  9.         @echo"Cleaning up..."
  10.         rm*.txt

make 命令會調用 say_hello 和 generate

  1. make
  2. HelloWorld
  3. Creatingempty text files...
  4. touchfile-{1..10}.txt

clean 不該該被放入 all 中,或者被放入第一個目標中。clean 應當在須要清理時手動調用,調用方法爲 make clean

  1. make clean
  2. Cleaning up...
  3. rm*.txt

如今你應該已經對 Makefile 有了基礎的瞭解,接下來咱們看一些進階的示例。

 

進階示例

 

變量

在以前的實例中,大部分目標和預置條件是已經固定了的,但在實際項目中,它們一般用變量和模式來代替。

定義變量最簡單的方式是使用 = 操做符。例如,將命令 gcc 賦值給變量 CC

  1. CC =gcc

這被稱爲遞歸擴展變量,用於以下所示的規則中:

  1. hello: hello.c
  2.     ${CC} hello.-o hello

你可能已經想到了,這些步驟將會在傳遞給終端時展開爲:

  1. gcc hello.-o hello

${CC} 和 $(CC) 都能對 gcc 進行引用。但若是一個變量嘗試將它自己賦值給本身,將會形成死循環。讓咱們驗證一下:

  1. CC =gcc
  2. CC = ${CC}
  3. all:
  4.     @echo ${CC}

此時運行 make 會致使:

  1. make
  2. Makefile:8:***Recursive variable 'CC' references itself (eventually).  Stop.

爲了不這種狀況發生,可使用 := 操做符(這被稱爲簡單擴展變量)。如下代碼不會形成上述問題:

  1. CC :=gcc
  2. CC := ${CC}
  3. all:
  4.     @echo ${CC}

 

模式和函數

下面的 Makefile 使用了變量、模式和函數來實現全部 C 代碼的編譯。咱們來逐行分析下:

  1. #Usage:
  2. #make        # compile all binary
  3. #make clean  # remove ALL binaries and objects
  4. .PHONY = all clean
  5. CC =gcc                        # compiler to use
  6. LINKERFLAG =-lm
  7. SRCS := $(wildcard *.c)
  8. BINS := $(SRCS:%.c=%)
  9. all: ${BINS}
  10. %:%.o
  11.         @echo"Checking.."
  12.         ${CC} ${LINKERFLAG} $<-o $@
  13. %.o:%.c
  14.         @echo"Creating object.."
  15.         ${CC}-c $<
  16. clean:
  17.         @echo"Cleaning up..."
  18.         rm-rvf *.o ${BINS}
  • 以 # 開頭的行是評論。
  • .PHONY = all clean 行定義了 all 和 clean 兩個僞目標。
  • 變量 LINKERFLAG 定義了在步驟中 gcc 命令須要用到的參數。
  • SRCS := $(wildcard *.c)$(wildcard pattern) 是與文件名相關的一個函數。在本示例中,全部 「.c」後綴的文件會被存入 SRCS 變量。
  • BINS := $(SRCS:%.c=%):這被稱爲替代引用。本例中,若是 SRCS 的值爲 'foo.c bar.c',則 BINS的值爲 'foo bar'
  • all: ${BINS} 行:僞目標 all 調用 ${BINS} 變量中的全部值做爲子目標。
  • 規則:

    1. %:%.o
    2.   @echo"Checking.."
    3.   ${CC} ${LINKERFLAG} $&lt;-o $@

    下面經過一個示例來理解這條規則。假定 foo 是變量 ${BINS} 中的一個值。% 會匹配到 foo%匹配任意一個目標)。下面是規則展開後的內容:

    1. foo: foo.o
    2.   @echo"Checking.."
    3.   gcc-lm foo.-o foo

    如上所示,% 被 foo 替換掉了。$< 被 foo.o 替換掉。$<用於匹配預置條件,$@ 匹配目標。對 ${BINS} 中的每一個值,這條規則都會被調用一遍。

  • 規則:

    1. %.o:%.c
    2.   @echo"Creating object.."
    3.   ${CC}-c $&lt;

    以前規則中的每一個預置條件在這條規則中都會都被做爲一個目標。下面是展開後的內容:

    1. foo.o: foo.c
    2.   @echo"Creating object.."
    3.   gcc-c foo.c
  • 最後,在 clean 目標中,全部的二進制文件和編譯文件將被刪除。

下面是重寫後的 Makefile,該文件應該被放置在一個有 foo.c 文件的目錄下:

    1. #Usage:
    2. #make        # compile all binary
    3. #make clean  # remove ALL binaries and objects
    4. .PHONY = all clean
    5. CC =gcc                        # compiler to use
    6. LINKERFLAG =-lm
    7. SRCS := foo.c
    8. BINS := foo
    9. all: foo
    10. foo: foo.o
    11.         @echo"Checking.."
    12.         gcc-lm foo.-o foo
    13. foo.o: foo.c
    14.         @echo"Creating object.."
    15.         gcc-c foo.c
    16. clean:
    17.         @echo"Cleaning up..."
    18.         rm-rvf foo.o foo
相關文章
相關標籤/搜索