在Makefile中,是支持函數使用的,Makefile中的函數包括make解釋器自身預定義的函數,同時也支持我們自己定義函數。
在Makefile中, 通過define關鍵字來實現函數的自定義,並以endef關鍵字結束,自定義函數使用預定義函數call調用,後邊跟自定義函數名及參數,如下就是一個簡單的自定義函數:
.PHONY : test
define fun1
@echo "My name is $(0)"
endef
define fun2
@echo "My name is $(0), param is $(1)"
endef
test:
$(call fun1)
$(call fun2, hello Makefile)
上邊我們首先定義了一個僞目標test,接着定義了兩個函數,fun1和fun2。fun1中直接通過$(0)輸出call 的第一個參數,也就是函數名fun1,fun2中同時輸出了第二個參數(這看起來有點 類似C語言裏的main函數參數)。 在調用自定義函數fun1和fun2時,格式爲$(call 函數名,參數...),call函數的參數需要以逗號","隔開,下邊 執行如下:
除了可以自定義函數外,make解釋器還提供了衆多預定的函數供我們使用,比方abspath(取文件的絕對路徑)
.PHONY : test2
var := $(abspath ./)
test2 :
@echo $(var)
這裏需要注意的是, 當我們使用make解釋器中預定義的 函數時,不需要通過call調用,而是直接$(函數名 參數1,參數2,...),下邊make一下:
可見,輸出了我當前路徑的絕對路徑。
上述僅僅是一些基礎知識,下邊就來使用make中的變量與函數來實現一個小綜合例子(會使用到一些預定義函數),要求如下:
1、自動生成target文件夾 存放可執行文件
2、自動生成objs文件夾存放編譯生成的目標文件(*.o)
3、 支持調試版本的 編譯選項
4、考慮代碼的可擴展性
在開始之前,首先來了解幾個關鍵技巧
1、自動獲取當前目錄下的源文件列表(預定義函數wildcard)
- SRCS := $(wildcard *.c)
2、由源文件列表 生成目標文件列表(變量的值變換)
-OBJS := $(SRCS : .c=.o)
3、爲每個 目標文件列表增加路徑前綴(預定義函數調用addprefix)
- OBJS := $(addprefix path/, $(OBJS))
4、規則的模式替換,這裏一共可以分爲兩種形式:
- 變量中的規則模式替換,如下
其中$(OBJS):%.o:%.c的意思就是,$(OBJS):%.o在變量OBJS中 逐個匹配後綴爲.o的文件,將匹配到的變量作爲目標,然後再將該變量的後綴由.o替換爲.c,作爲目標的依賴。合成的 步驟就是$(OBJS):%.o:%.c -> func.o:%.c -> func.o:func.c,這樣變形成了func.o爲目標,func.c爲依賴的一條規則,同理main.o與main.c的目標依賴關係也是 這麼形成的。
- 目錄中的規則模式替換,目錄與變量的主要區別就是一個是從當前目錄中去匹配,一個是從變量中去匹配。
這裏我們可以看到目錄中的規則模式匹配,省去的目標 變量$(OBJS),而是直接 %.o:%.c 從 當前目中去進行.o文件的模式匹配。
對於以上5點技巧,我們以一個簡單的實例來展示:
.PHONY : all clean
CC := gcc
SRCS := $(wildcard *.c)
OBJS := $(SRCS:.c=.o)
OBJSPATH := $(addprefix path/, $(OBJS))
all: $(OBJS)
@echo "SRCS => $(SRCS)"
@echo "OBJS => $(OBJS)"
@echo "OBJSPATH => $(OBJSPATH)"
#變量中的規則模式替換
#$(OBJS):%.o:%.c
# @echo $(CC) -o $@ -c $^
#目錄結構的規則模式替換
%.o:%.c
@echo $(CC) -o $@ -c $^
clean:
$(RM) -f *.o
make的結果如下,可以看到,我們使用的這些技巧,能可以方便的幫助我們編寫易於維護的Makefile,當項目工程越來越大時,這就顯得尤爲重要:
有了以上的知識我們開始編寫綜合實例,當前目錄下有如下文件:
Makefile文件如下:
CC := gcc
MKDIR := mkdir
RM := rm -rf
DIR_OBJS := objs
DIR_TARGET := target
DIRS := $(DIR_OBJS) $(DIR_TARGET)
TARGET := $(DIR_TARGET)/main.out
# main.c fun.c
SRCS := $(wildcard *.c)
# main.o fun.o
OBJS := $(SRCS:.c=.o)
#./objs/main.o ./objs/fun.o
OBJS := $(addprefix $(DIR_OBJS)/, $(OBJS))
.PHONY : rebuild all clean
$(TARGET) : $(DIRS) $(OBJS)
$(CC) -o $@ $(OBJS)
@echo "target file => $@"
$(DIRS):
$(MKDIR) $@
#debug與release版本編譯選擇
#目錄結構的規則模式替換,因爲.o文件在objs目錄下,所以要加objs路徑
$(DIR_OBJS)/%.o:%.c
ifeq ($(DEBUG),true)
$(CC) -o $@ -g -c $^
else
$(CC) -o $@ -c $^
endif
rebuild : clean all
all : $(TARGET)
clean:
$(RM) $(DIRS)
下邊分別來編譯debug版本與release版本,編譯debug版本時在命令行後加DEBUG := true即可
編譯成功後的目錄結構如下
可以看到,多出了objs與target兩個文件夾,裏邊的內容分別爲.o目標文件與.out可執行文件
到此,我們的一個小小的可維護的Makefile綜合實例已經完成,更多的內容待後續慢慢完善。