9月 24, 2009

Makefile 語法

雖然 Makefile 之前也用過,不過這次複雜多了。主要是因為「原始檔」與「標頭檔」目錄位置不同所造成的。樹狀圖是檔案分佈的狀態:
[project]
+
+--- main.c ... main(位於根目錄下)
+--- [src] ... 所有 source 檔
+--- [inc] ... 部份 header 檔
+--- [h] ... 主要 header 檔
+--- [o] ... 編譯出的 object 檔
根目錄最終只會有:執行檔(Binary file)與 main.c 原始檔,所有編譯出來的的 .o 檔都要存放在 [o] 下面。編譯時要引入 [inc] 與 [h] 目錄下的標頭檔。

接著列出 Makefile 內建的自動變數(Automatic Variables):
$@ - 目標檔名(Target)
$< - 第一個相依條件名稱(First prerequisite)
$^ - 所有相依條件名稱(All prerequisites)
$? - 同上但時間晚於 target 者
另外在 Makefile 中常用的函式:
(1) patsubst - 取代字串中的特定樣式,搭配萬用字元 % 使用。
語法:$(patsubst pattern, replacement, text)
範例:$(patsubst %.c , %.o , main.c) 可得到 main.o
(2) wildcard - 展開變數中的萬用字元,搭配 * ? 使用。
語法:$(wildcard pattern)
範例:$(wildcard *.c)
這次使用的 Makefile 全貌:
PROG	= cal
INC_DIR = h inc
SRC_DIR = src
OBJ_DIR = o

CC = gcc
CFLAGS = -g -c
INC = $(patsubst %,-I%,$(INC_DIR))

MAIN_SRC = main.c
MAIN_OBJ = $(OBJ_DIR)/$(MAIN_SRC:.c=.o) #...[1]
SRC_FILES = $(wildcard $(SRC_DIR)/*.c)
#OBJ_FILES = $(wildcard $(OBJ_DIR)/*.o) #...[2]
OBJ_FILES = $(patsubst $(SRC_DIR)/%.c, $(OBJ_DIR)/%.o, $(SRC_FILES))

all: $(PROG)

$(PROG): $(OBJ_FILES) $(MAIN_OBJ)
$(CC) -o $@ $^

#(OBJ_FILES) : $(SRC_DIR)/%.c #...[3]
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c
$(CC) $(CFLAGS) $(INC) -o $@ $<

$(MAIN_OBJ): $(MAIN_SRC)
$(CC) $(CFLAGS) $(INC) -o $@ $<

clean:
@rm -f $(PROG).exe $(OBJ_FILES) $(MAIN_OBJ)

其中要注意的地方有三個:

(1) MAIN_OBJ = $(OBJ_DIR)/$(MAIN_SRC:.c=.o)
是指將 $(MAIN_SRC) 內的 .c 換成 .o

(2) OBJ_FILES = $(wildcard $(OBJ_DIR)/*.o)
完全錯誤寫法,因為一開始 .o 還未編譯出來,所以 OBJ_FILES 只會得到空值。

(3) 取代語法要前後搭配,萬用字元 % 才有意義。
(OBJ_FILES): $(SRC_DIR)/%.c  ...應該改成
$(OBJ_DIR)/%.o: $(SRC_DIR)/%.c

沒有留言:

張貼留言