(九):目錄搜索
在一個較大的工程中,一般會將源代碼和二進制文件(.o 文件和可執行文件)安排在不同的目錄來進行區分管理。這種情況下,我們可以使用 make 提供的目錄搜索依賴文件功能(在指定的若干個目錄下自動搜索依賴文件)。在Makefile中,使用依賴文件的目錄搜索功能。當工程的目錄結構發生變化後,就可以做到不更改 Makefile的規則,只更改依賴文件的搜索目錄。
在我們上一節出現的問題當中,我們將.c文件統一放在src目錄下,沒有和Makefile目錄在同一目錄下,因此沒有辦法尋找到.o文件的依賴文件。make程序有一個特殊的變量VPATH,該變量可以指定依賴文件的搜索路徑,當規則的依賴文件在當前目錄不存在時,make 會在此變量所指定的目錄下去尋找這些依賴文件。通常我們都是用此變量來指定規則的依賴文件的搜索路徑。
定義變量 “VPATH”時,使用空格或者冒號(:)將多個需要搜索的目錄分開。make搜索目錄的順序是按照變量“VPATH”定義中的目錄順序進行的,當前目錄永遠是第一搜索目錄。例如如下定義
VPATH += ./src
指定了依賴搜索目錄為當前目錄下的src目錄,我們可以在Makefile.rules裡面添加給VPATH變量賦值,而在包含該Makefile.rules之前給出當前模塊.c文件所在目錄。
其實我們也可以直接指定依賴文件的路徑,這樣也是可以的,如下:
$(SRC_OBJ) : $(OBJDIR)/%.o : $(MOD_SRC_DIR)/%.c
>---$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
但是這樣在我們更改了工程目錄結構之後,對應的依賴文件沒有在同一目錄下,又變得麻煩了,所以還不如直接給VPATH變量賦值,我們只需要指定源碼所在的目錄即可。
其實我們還有另外一種搜索文件路徑方法:使用vpath關鍵字(注意不是VPATH變量), 它和VPATH類似,但是它可以為不同類型的文件(由文件名區分)指定不同的搜索目錄。使用方法有三種:
1、vpath PATTERN DIRECTORIES 為所有符合模式“PATTERN”的文件指定搜索目錄“DIRECTORIES” 。多個目錄使用空格或者冒號(:)分開。
2、vpath PATTERN 清除之前為符合模式“PATTERN”的文件設置的搜索路徑。
3、vpath 清除所有已被設置的文件搜索路徑。 vapth 使用方法中的“PATTERN”需要包含模式字符“%”;例如上面的定義:
VPATH += ./src
可以寫為:
vpath %.c ./src
現在給一個我們的Makefile.rules:
# Copyright (C) 2014 shallnew \at 163 \dot com
# if without a platform defined, give value "unknow" to PLATFORM
ifndef PLATFORM
>---PLATFORM = unknow
endif
# define a root build directory base on the platform
# if without a SRC_BASE defined, just use local src directory
ifeq ($(SRC_BASE),)
>---BUILDDIR = $(MOD_SRC_DIR)
>---OBJDIR = $(MOD_SRC_DIR)
>---LIBDIR = $(MOD_SRC_DIR)
>---BINDIR = $(MOD_SRC_DIR)
else
>---ifeq ($(DEBUG_SYMBOLS), TRUE)
>--->---BUILDDIR = $(SRC_BASE)/build/$(PLATFORM)_dbg
>---else
>--->---BUILDDIR = $(SRC_BASE)/build/$(PLATFORM)
>---endif
>---OBJDIR = $(BUILDDIR)/obj/$(MODULE)
>---LIBDIR = $(BUILDDIR)/lib
>---BINDIR = $(BUILDDIR)/bin
endif
# update compilation flags base on "DEBUG_SYMBOLS"
ifeq ($(DEBUG_SYMBOLS), TRUE)
>---CFLAGS += -g -Wall -Werror -O0
else
>---CFLAGS += -Wall -Werror -O2
endif
VPATH += $(MOD_SRC_DIR)
SRC_OBJ = $(patsubst %.c, $(OBJDIR)/%.o, $(notdir $(SRC_FILES)))
ifeq ($(MAKELEVEL), 0)
all : msg
else
all : lib bin
endif
lib : $(OBJDIR) $(LIBDIR)/$(SRC_LIB)
bin : $(OBJDIR) $(BINDIR)/$(SRC_BIN)
$(OBJDIR) :
>---mkdir -p $@
ifneq ($(SRC_BIN),)
$(BINDIR)/$(SRC_BIN) : $(SRC_OBJ)
>---$(CC) -o $@ $^ $(LDFLAGS)
endif
ifneq ($(SRC_LIB),)
$(LIBDIR)/$(SRC_LIB) : $(SRC_OBJ)
>---$(AR) rcs $@ $^
>---cp $@ $(SRC_BASE)/libs
endif
$(SRC_OBJ) : $(OBJDIR)/%.o : %.c
>---$(CC) -c $(CFLAGS) $(CPPFLAGS) $< -o $@
msg:
>---@echo "You cannot directily execute this Makefile! This Makefile should called by toplevel Makefile."
# clean target
clean:
ifneq ($(SRC_LIB),)
>--->---$(RM) $(SRC_OBJ) $(LIBDIR)/$(SRC_LIB)
endif
ifneq ($(SRC_BIN),)
>--->---$(RM) $(SRC_OBJ) $(BINDIR)/$(SRC_BIN)
endif
.PHONY : all clean