Makefile 入門教學

2023-10-10 12:03:48

Makefile 是一個非常強大的構建自動化工具,用於管理專案的編譯、連結和其他構建任務。以下是一個詳細的 Makefile 使用檔案,包括基本概念、語法、範例和常見任務。

1. 基本概念

  • 目標 (Targets):在 Makefile 中,目標是要生成的檔案或執行的操作的名稱。目標可以是檔名,也可以是偽目標,用於執行特定任務而不生成檔案。
  • 依賴項 (Dependencies):依賴項是與目標相關聯的檔案或其他目標,它們在目標生成之前必須存在或已經生成。
  • 規則 (Rules):規則定義瞭如何生成目標以及生成目標所需的命令。
  • 命令 (Commands):命令是在生成目標時要執行的操作。命令必須以 Tab 鍵開頭。
  • 變數 (Variables):變數用於儲存文字或命令,並可以在整個 Makefile 中重複使用。
  • 偽目標 (Phony Targets):偽目標是不代表實際檔案的目標,而是用於執行特定操作的標記。

2. Makefile 語法

一個基本的 Makefile 規則的語法如下:

target: dependencies
    command
  • target:要生成的目標的名稱。
  • dependencies:生成目標所需的檔案或其他目標的列表。
  • command:生成目標的命令,必須以 Tab 鍵開頭。

3. 範例 Makefile

以下是一個簡單的範例 Makefile,用於編譯一個 C 程式:

CC = gcc
CFLAGS = -Wall
TARGET = myprogram
SOURCES = main.c utils.c

$(TARGET): $(SOURCES)
    $(CC) $(CFLAGS) -o $(TARGET) $(SOURCES)

clean:
    rm -f $(TARGET)

這個 Makefile 使用了變數 CCCFLAGSTARGETSOURCES,並定義了一個 all 目標用於編譯程式,以及一個 clean 目標用於清理生成的檔案。

4. 常見任務

4.1 編譯專案

all: $(TARGET)

$(TARGET): $(SOURCES)
    $(CC) $(CFLAGS) -o $(TARGET) $(SOURCES)

4.2 清理生成的檔案

clean:
    rm -f $(TARGET)

4.3 使用變數

CC = gcc
CFLAGS = -Wall
TARGET = myprogram
SOURCES = main.c utils.c

$(TARGET): $(SOURCES)
    $(CC) $(CFLAGS) -o $(TARGET) $(SOURCES)

4.4 偽目標

.PHONY: clean

clean:
    rm -f $(TARGET)

5. 高階用法

除了基本用法外,還支援一些高階用法,可以用來處理更復雜的構建需求。以下是一些高階用法的詳細介紹:

5.1 條件語句和函數

條件語句

Makefile 支援條件語句,可以根據條件來執行不同的規則或命令。通常使用 ifeqifdef 這兩個條件語句。

ifeq ($(VARIABLE), value)
    # 條件為真時的規則和命令
else
    # 條件為假時的規則和命令
endif

例如,可以根據是否定義了 DEBUG 變數來設定不同的編譯選項:

ifeq ($(DEBUG), 1)
    CFLAGS = -g
else
    CFLAGS = -O2
endif
函數

Makefile 還提供了一些內建函數,用於處理文字和檔案列表。以下是一些常見的函數:

  • $(shell command):執行 shell 命令並返回結果。
  • $(wildcard pattern):匹配檔名模式並返回符合條件的檔案列表。
  • $(foreach var, list, text):對列表中的每個元素執行指定的操作。
  • $(strip string):刪除字串開頭和結尾的空白字元。
  • $(subst find,replace,text):替換文字中的字串。
  • $(filter pattern, text):從文字中篩選出匹配指定模式的字串。
  • $(patsubst pattern,replacement,text):用指定字串替換文字中的模式。
  • $(notdir names):從檔案路徑中提取檔名。

這些函數可以在 Makefile 中用於各種目的,例如檔案操作、文書處理和條件判斷。

5.2 自動化依賴關係生成

通常,Makefile 中的依賴關係需要手動維護。但是,對於 C/C++ 專案,您可以使用編譯器提供的 -M 選項來自動生成依賴關係。例如:

SOURCES = main.c utils.c
DEPS = $(SOURCES:.c=.d)

%.d: %.c
    $(CC) -M $< -o $@

-include $(DEPS)

在這個範例中,%.d: %.c 規則用於自動生成 .d 檔案,其中包含了 .c 檔案的依賴關係。然後使用 -include 指令來包含這些 .d 檔案,以自動跟蹤依賴關係。

5.3 多目錄專案

對於大型專案,通常需要將 Makefile 拆分成多個子目錄,每個子目錄都有自己的 Makefile。然後,可以使用遞迴或變數傳遞來管理這些子目錄之間的依賴關係。例如:

SUBDIRS = dir1 dir2

all: $(SUBDIRS)

$(SUBDIRS):
    $(MAKE) -C $@

clean:
    for dir in $(SUBDIRS); do \
        $(MAKE) -C $$dir clean; \
    done

在這個範例中,SUBDIRS 變數包含了子目錄的列表。$(MAKE) 是一個 Makefile 中的特殊變數,用於啟動另一個 Makefile。

5.4 高階變數操作

Makefile 支援高階的變數操作,包括字串操作、條件賦值、變數展開等。以下是一些範例:

字串操作
STR1 = hello
STR2 = world
STR3 = $(STR1) $(STR2)

在這個範例中,STR3 的值將是 "hello world"

條件賦值
FOO ?= default_value

如果 FOO 變數未定義,則將其賦值為 default_value

變數展開
VAR1 = $(VAR2)
VAR2 = value

在這個範例中,VAR1 的值將是 value,因為 Make 會遞迴地展開變數。


孟斯特

宣告:本作品採用署名-非商業性使用-相同方式共用 4.0 國際 (CC BY-NC-SA 4.0)進行許可,使用時請註明出處。
Author: mengbin
blog: mengbin
Github: mengbin92
cnblogs: 戀水無意