通用makefile是如何煉成的(VIII)

 停在這裏不敢走下去,     讓悲傷無法上演

 下一頁你親手寫上的離別, 由不得我拒絕

 這條路我們走得太匆忙,  擁抱着並不真實的慾望,   來不及等不及回頭欣賞

大笑摘自《步步驚心 》片尾曲 《三寸天堂》)


走到這裏,實際上我們的makefile框架已經是搭得差不多了。這一路小跑過來,感覺一切都很美好,實際上我想,我們只是太匆忙,來不及細細回想。

如果真的回首詳觀,各種瑣碎細節會讓你崩潰。

無論如何,該面對的終是要面對的


第一個工作是,讓我們的module.mk更像是一個模塊mk,爲此引入MODULE_NAME, MODULE_PATH. 這兩項結合起來,以後也可以作爲module的唯一標識。

hello模塊的module.mk

# module.mk
#
MODULE_PATH			:= $(call current_path)
MODULE_NAME			:= hello


獲取模塊路徑時,我用了一個小小的技巧。很多書上都有提到,原理是利用了makefile的環境變量MAKEFILE_LIST.

這裏簡單貼一下實現代碼

define current_path
$(patsubst %/,%, \
$(dir $(word $(words $(MAKEFILE_LIST)), $(MAKEFILE_LIST))))
endef


D第二個我們要做的瑣事是調整一些路徑。現在的makefile,編譯生成的中間文件還是和源文件混雜在一起的,爲了美觀和方便版本控制,我們把它們集中到INTERMEDIATE_OBJ_PATH,對其他的一些自動生成的文件,也是類似處理。

做這項工作時注意要”步步驚心“, 還好我們是從一開始就知道故事結局的大笑,就是要正確編譯得到可執行文件。


另外,不同的人,可以對路徑的處理不同。例如,爲了區分不同產品,不同平臺,有些同學喜歡爲各個產品、各個平臺分別建立文件夾,構成深目錄層次, 有些同學喜歡採用 <product>--<platform>這樣的淺層次的目錄結構。

這裏選擇了淺目錄形式,因爲大部分情況下我們是不需要多平臺的。


這裏爲了方便測試,特意引入了產品信息TEST, 平臺信息X86



所有的路徑信息,統一集中在paths/path.mk。這裏引入兩個重要概念TARGET_PRODUCT, TARGET_PLATFORM,這是由make命令行參數傳入的。這裏爲了測試方便,直接在腳本中寫入了。

# path.mk


## 目前的路徑策略,根目錄下建立out,bin,lib,src,build五大目錄,out目錄下可以再建立bin.lib,obj目錄

PROJECT_OUT_PATH 		:= out
PROJECT_BIN_PATH 		:= bin
PROJECT_LIB_PATH 		:= lib

TARGET_PRODUCT := TEST
TARGET_PLATFORM:= X86


ifneq "$(TARGET_PRODUCT)" ""
  PRODUCT_SUFFIX 		:= $(TARGET_PRODUCT)
else
  PRODUCT_SUFFIX 		:= 
endif

ifneq "$(TARGET_PLATFORM)" ""
  PRODUCT_SUFFIX        := $(PRODUCT_SUFFIX)-$(TARGET_PLATFORM)
endif

## 產品和平臺附加的路徑後綴不爲空,就以此建立子目錄
ifneq "$(PRODUCT_SUFFIX)" ""
PRODUCT_OUT_PATH 		:= $(PROJECT_OUT_PATH)/$(PRODUCT_SUFFIX)
PRODUCT_BIN_PATH 		:= $(PROJECT_BIN_PATH)/$(PRODUCT_SUFFIX)
PRODUCT_LIB_PATH 		:= $(PROJECT_LIB_PATH)/$(PRODUCT_SUFFIX)
else
PRODUCT_OUT_PATH 		:= $(PROJECT_OUT_PATH)
PRODUCT_BIN_PATH 		:= $(PROJECT_BIN_PATH)
PRODUCT_LIB_PATH 		:= $(PROJECT_LIB_PATH)
endif

INSTALL_PATH := $(PRODUCT_BIN_PATH)
INTERMEDIATE_PATH := $(PRODUCT_OUT_PATH)
INTERMEDIATE_OBJS_PATH:=$(INTERMEDIATE_PATH)/obj
INTERMEDIATE_LIB_PATH:=$(INTERMEDIATE_PATH)/lib
INTERMEDIATE_BIN_PATH:=$(INTERMEDIATE_PATH)/bin

$(info INSTALL_PATH=$(INSTALL_PATH))
$(info INTERMEDIATE_PATH=$(INTERMEDIATE_PATH))

配置後,注意同步調整targets目錄下的mk文件,利用object.mk

# object.mk

## 從源文件列表中分別提取出C文件和C++文件
C_OBJS  := $(patsubst %.c, $(INTERMEDIATE_OBJS_PATH)/%.o, $(filter %.c, $(SRC_FILES)))
CPP_OBJS:= $(patsubst %.cpp, $(INTERMEDIATE_OBJS_PATH)/%.o, $(filter %.cpp, $(SRC_FILES)))
OBJS    := $(C_OBJS) $(CPP_OBJS)
DEPS    := $(OBJS:.o=.d)


## -MMD 可以用於自動生成頭文件依賴關係
$(INTERMEDIATE_OBJS_PATH)/%.o : %.cpp
	$(hide)$(MAKEDIR) $(dir $@)
	$(CXX) $(CPPFLAGS) $(CXXFLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$@" -MT"$(@:%.o=%.d)" -c "$<" -o "$@" 
$(INTERMEDIATE_OBJS_PATH)/%.o : %.c
	$(hide)$(MAKEDIR) $(dir $@)
	$(CC) $(CPPFLAGS) $(CFLAGS) -MMD -MP -MF"$(@:%.o=%.d)" -MT"$@" -MT"$(@:%.o=%.d)" -c "$<" -o "$@" 
....


另外,如果在module.mk中沒有定義目標時,我們根據模塊名自動生成目標

# executable.mk
#

## 編譯生成obj文件的通用規則
include build/targets/object.mk

## 如果目標未定義,則默認是out/bin/<module-name>
ifeq "$(TARGET)" ""
  TARGET := $(INTERMEDIATE_BIN_PATH)/$(MODULE_NAME)
endif

。。。。


今天的工作到此爲止。主要是瑣事纏身




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