第八載:makefile中函數定義及調用

    在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綜合實例已經完成,更多的內容待後續慢慢完善。

發表評論
所有評論
還沒有人評論,想成為第一個評論的人麼? 請在上方評論欄輸入並且點擊發布.
相關文章