1. 程式人生 > 其它 >【作業系統】Makefile

【作業系統】Makefile

make 是一個根據指定的 Shell 命令進行構建的工具——你規定要構建哪個檔案、它依賴哪些原始檔,當那些檔案有變動時,如何重新構建它。


Makefile 格式

<target> : <prerequisites> 
[tab]  <commands>

目標

  • 目標可以是一個檔名,也可以是多個檔名,之間用空格分隔;除了檔名,目標還可以是某個操作的名字。
  • 如果 make 命令執行時沒有指定目標,預設會執行 Makefile 檔案的第一個目標。
  • 偽目標 .PHONY:宣告某個“偽目標”後,make 就不會去檢查是否存在一個叫做“偽目標”的檔案,也不會檢查其依賴的修改時間,每次執行都執行對應的命令。

依賴

  • 前置檔案(依賴)通常是一組檔名,之間用空格分隔。
  • 指定了"目標"是否重新構建的判斷標準:只要有一個前置檔案不存在,或者有過更新(前置檔案的 last modification 時間戳比目標的時間戳新),"目標"就需要重新構建。
  • 依賴可以省略。
all: test.txt
	echo hello
test.txt:
	echo world > test.txt

若當前目錄中沒有 test.txt,在 Shell 中執行 make 將會先執行 echo world > test.txt,隨後執行 echo hello


規則

  • 規則是構建"目標"的具體指令。

  • 每行命令在一個單獨的 Shell 中執行,這些 Shell 之間沒有繼承關係;若想讓命令間有繼承關係:

    • 將兩行命令寫在一行,中間用分號分隔;

    • 或在換行符前加反斜槓轉義;

      all:
      	mkdir a;
      	cd a; \
      	touch b;
      
    • 或使用 .ONESHELL: 命令,Mac 自帶 Make 3.81 不支援 .ONESHELL:,需要:

      • brew instal make
      • open ~/zshrc
      • export PATH 中新增 /usr/local/bin/usr/local/opt/make/libexec/gnubin,使用 : 分隔,如:export PATH=/usr/local/bin:/usr/local/opt/make/libexec/gnubin:$PATH
  • 每行命令之前必須有一個[tab]

    鍵。如果想用其他鍵,可以用內建變數 .RECIPEPREFIX 宣告。

.RECIPEPREFIX=>
.ONESHELL:
all:
>mkdir a
>cd a
>touch b

Makefile 語法


回聲

  • 正常情況下,make 會列印每條命令,然後再執行,這就叫做回聲(echoing)。
  • 在命令的前面加上 @,就可以關閉回聲。

萬用字元

  • 用來指定一組符合條件的檔名。
  • Makefile 的萬用字元與 Bash 一致,主要有星號 *、問號 ?[...]
    • 萬用字元 ?:匹配一個任意字元。
    • 萬用字元 *: 匹配0個或任意多個字元,也就是可以匹配任何內容。
      • 例:對於檔案 foo, foo1, foo2, foo10, bar:
      • rm foo? 刪除 foo1 和 foo2。
      • rm foo* 刪除除 bar 外的所有檔案。
    • 萬用字元 [ ]:匹配中括號中任意一個字元。[-] 代表範圍,[^] 代表邏輯非。
      • 例:[abc] 匹配 abc[a-z] 匹配所有小寫字母;[^0-9] 匹配一個不是數字的字元。

模式匹配

  • make 命令允許對檔名,進行類似正則運算的匹配,主要用到的匹配符是 %
  • 使用匹配符 %,可以將大量同類型的檔案,只用一條規則就完成構建。
  • 咕咕咕~

變數和賦值

賦值:

  • VAR=value:在執行時(引用時)擴充套件,允許遞迴擴充套件。

    varA = hi
    varB = $(varA)
    varA = hello
    all:
    	@echo $(varB)
    # 輸出 hello
    
  • VAR:=value:在定義時(賦值時)擴充套件。

    varA := hi
    varB := $(varA)
    varA := hello
    all:
    	@echo $(varB)
    # 輸出 hi
    
  • VAR?=value:只有在該變數為空時才設定值。

  • VAR+=value:將值追加到變數的尾端。

呼叫:

  • 變數需要放在 $( ) 之中。
  • 呼叫 Shell 變數,需要在美元符號前,再加一個美元符號,如 $$HOME

內建變數

  • $(CC) 指向當前使用的編譯器。
  • $(MAKE) 指向當前使用的 make 工具。

自動變數

  • $@:指代當前“目標”。
  • $<:指代第一個“依賴”。
  • $?:指代比“目標”新的所有“依賴”。
  • $^:指代所有“依賴”。
  • $*:指代匹配符 % 匹配的部分。
  • $(@D):指向 $@ 的目錄名。
  • $(@F):指向 $@ 的檔名。
  • $(<D):指向 $< 的目錄名。
  • $(<F):指向 $< 的檔名。

判斷和迴圈

判斷:

  • 使用 ifeqelse endif,沒有縮排。
all:
ifeq ($(CC),gcc)
	@echo hi
else
	@echo hello
endif
# 輸出 hello
  • 使用 Bash 語法,有縮排。
.ONESHELL:
all:
	@if [ 1 == 2 ]
	@then
		@echo hi
	@else
		@echo hello
	@fi
# 輸出 hello

迴圈:

.ONESHELL:
LIST = one two three
all:
	@for i in $(LIST)
	@do 
		@echo $$i; 
	@done

函式

  • 呼叫方法:$(function arguments)${function arguments}

  • 內建函式:咕咕咕~


參考