1.问题:如果我们正在进行的项目需要使用第三方库,那么前几节的makefile能胜任吗?
答案:是否定的。
2.经验假设
本节我们研究第三方库的使用。使makefile支持第三方库。
经验假设:
-
第三方库通过函数调用的方式提供库中的功能
-
库文件发布时都附带了声明库函数原型的头文件
-
编译阶段使用头文件,链接阶段使用库文件
第三方库在项目中的位置:
libs第三方库一般和其他模块是并列的关系。因此,libs文件夹和commom、module等文件夹是同等地位的。动态链接库和静态链接库都有对应的头文件。
3.第三方库的编译阶段支持
注意事项:
实际工程中先将第三方库拷贝到编译文件夹再进行链接,libs文件夹和其他模块地位等同,把它当做源文件夹使用,拷贝的结果当做编译结果。
4.第三方库的链接阶段支持
链接阶段:
第三方库必须当做最后一个依赖,这是为了处理一种极端的情况:我们的一个子模块和库文件的名字相同。
在这种情况下,我们优先使用自己的模块而不是第三方库模块。
5.代码架构
代码结构:
libs/lib下的内容:
顶层makefile:
1 include pro-cfg.mk 2 include cmd-cfg.mk 3 include pro-rule.mk
cmd-cfg.mk:
1 AR := ar 2 ARFLAGS := crs 3 4 CC := gcc 5 LFLAGS := 6 CFLAGS := -I$(DIR_INC) -I$(DIR_COMMON_INC) -I$(DIR_LIBS_INC) 7 8 ifeq ($(DEBUG),true) 9 CFLAGS += -g 10 endif 11 12 MKDIR := mkdir 13 RM := rm -fr 14 CP := cp
第六行加上了第三方库相关的支持。
pro-cfg.mk:
1 MODULES := common 2 module 3 main 4 5 MOD_CFG := mod-cfg.mk 6 MOD_RULE := mod-rule.mk 7 CMD_CFG := cmd-cfg.mk 8 9 DIR_BUILD := build 10 DIR_COMMON_INC := common/inc 11 DIR_LIBS_INC := libs/inc 12 DIR_LIBS_LIB := libs/lib 13 14 APP := app.out
第11、12行新增了第三方库相关的变量。
pro-rule.mk:
1 .PHONY : all compile link clean rebuild $(MODULES) 2 3 DIR_PROJECT := $(realpath .) 4 DIR_BUILD_SUB := $(addprefix $(DIR_BUILD)/, $(MODULES)) 5 MODULE_LIB := $(addsuffix .a, $(MODULES)) 6 MODULE_LIB := $(addprefix $(DIR_BUILD)/, $(MODULE_LIB)) 7 EXTERNAL_LIB := $(wildcard $(DIR_LIBS_LIB)/*) 8 EXTERNAL_LIB := $(patsubst $(DIR_LIBS_LIB)/%, $(DIR_BUILD)/%, $(EXTERNAL_LIB)) 9 10 APP := $(addprefix $(DIR_BUILD)/, $(APP)) 11 12 define makemodule 13 cd $(1) && 14 $(MAKE) all 15 DEBUG:=$(DEBUG) 16 DIR_BUILD:=$(addprefix $(DIR_PROJECT)/, $(DIR_BUILD)) 17 DIR_COMMON_INC:=$(addprefix $(DIR_PROJECT)/, $(DIR_COMMON_INC)) 18 DIR_LIBS_INC:=$(addprefix $(DIR_PROJECT)/, $(DIR_LIBS_INC)) 19 CMD_CFG:=$(addprefix $(DIR_PROJECT)/, $(CMD_CFG)) 20 MOD_CFG:=$(addprefix $(DIR_PROJECT)/, $(MOD_CFG)) 21 MOD_RULE:=$(addprefix $(DIR_PROJECT)/, $(MOD_RULE)) && 22 cd .. ; 23 endef 24 25 all : compile $(APP) 26 @echo "Success! Target ==> $(APP)" 27 28 compile : $(DIR_BUILD) $(DIR_BUILD_SUB) 29 @echo "Begin to compile ..." 30 @set -e; 31 for dir in $(MODULES); 32 do 33 $(call makemodule, $$dir) 34 done 35 @echo "Compile Success!" 36 37 link $(APP) : $(MODULE_LIB) $(EXTERNAL_LIB) 38 @echo "Begin to link ..." 39 $(CC) -o $(APP) -Xlinker "-(" $^ -Xlinker "-)" $(LFLAGS) 40 @echo "Link Success!" 41 42 $(DIR_BUILD)/% : $(DIR_LIBS_LIB)/% 43 $(CP) $^ $@ 44 45 $(DIR_BUILD) $(DIR_BUILD_SUB) : 46 $(MKDIR) $@ 47 48 clean : 49 @echo "Begin to clean ..." 50 $(RM) $(DIR_BUILD) 51 @echo "Clean Success!" 52 53 rebuild : clean all 54 55 $(MODULES) : $(DIR_BUILD) $(DIR_BUILD)/$(MAKECMDGOALS) 56 @echo "Begin to compile $@" 57 @set -e; 58 $(call makemodule, $@) 59
第7、8行新增了第三方库文件列表,第18行将第三方库相关的变量传到模块的makefile。第45、46行是库文件的拷贝操作。
mod-cfg.mk:
1 DIR_SRC := src 2 DIR_INC := inc 3 4 TYPE_INC := .h 5 TYPE_SRC := .c 6 TYPE_OBJ := .o 7 TYPE_DEP := .dep
mod-rule.mk:
1 .PHONY : all 2 3 MODULE := $(realpath .) 4 MODULE := $(notdir $(MODULE)) 5 DIR_OUTPUT := $(addprefix $(DIR_BUILD)/, $(MODULE)) 6 7 OUTPUT := $(MODULE).a 8 OUTPUT := $(addprefix $(DIR_BUILD)/, $(OUTPUT)) 9 10 SRCS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC)) 11 OBJS := $(SRCS:$(TYPE_SRC)=$(TYPE_OBJ)) 12 OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(OBJS)) 13 DEPS := $(SRCS:$(TYPE_SRC)=$(TYPE_DEP)) 14 DEPS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(DEPS)) 15 16 vpath %$(TYPE_INC) $(DIR_INC) 17 vpath %$(TYPE_INC) $(DIR_COMMON_INC) 18 vpath %$(TYPE_INC) $(DIR_LIBS_INC) 19 vpath %$(TYPE_SRC) $(DIR_SRC) 20 21 -include $(DEPS) 22 23 all : $(OUTPUT) 24 @echo "Success! Target ==> $(OUTPUT)" 25 26 $(OUTPUT) : $(OBJS) 27 $(AR) $(ARFLAGS) $@ $^ 28 29 $(DIR_OUTPUT)/%$(TYPE_OBJ) : %$(TYPE_SRC) 30 $(CC) $(CFLAGS) -o $@ -c $(filter %$(TYPE_SRC), $^) 31 32 33 $(DIR_OUTPUT)/%$(TYPE_DEP) : %$(TYPE_SRC) 34 @echo "Creating $@ ..." 35 @set -e; 36 $(CC) $(CFLAGS) -MM -E $(filter %$(TYPE_SRC), $^) | sed 's,(.*).o[ :]*,$(DIR_OUTPUT)/1$(TYPE_OBJ) $@ : ,g' > $@
common文件夹下的makefile:
1 include $(MOD_CFG) 2 3 # Custmization Begin 4 # 5 # DIR_SRC := src 6 # DIR_INC := inc 7 # 8 # TYPE_INC := .h 9 # TYPE_SRC := .c 10 # TYPE_OBJ := .o 11 # TYPE_DEP := .dep 12 # 13 # Custmization End 14 15 include $(CMD_CFG) 16 17 include $(MOD_RULE)
main文件夹下的makefile:
1 include $(MOD_CFG) 2 3 # Custmization Begin 4 # 5 # DIR_SRC := src 6 # DIR_INC := inc 7 # 8 # TYPE_INC := .h 9 # TYPE_SRC := .c 10 # TYPE_OBJ := .o 11 # TYPE_DEP := .dep 12 # 13 # Custmization End 14 15 include $(CMD_CFG) 16 17 include $(MOD_RULE)
module文件夹下的makefile:
1 include $(MOD_CFG) 2 3 # Custmization Begin 4 # 5 # DIR_SRC := src 6 # DIR_INC := inc 7 # 8 # TYPE_INC := .h 9 # TYPE_SRC := .c 10 # TYPE_OBJ := .o 11 # TYPE_DEP := .dep 12 # 13 # Custmization End 14 15 include $(CMD_CFG) 16 17 include $(MOD_RULE)
如果我们突然不使用第三方库了,则只需删掉libs文件夹下的内容即可,而makefile无需改动。
6.小结:
-
编译环境必须支持第三方库的使用(静态库或动态库)
-
工程开发中一般会使用特殊的文件夹存放第三方库
-
第三方库所附带的头文件用于声明库文件(编译阶段需要)
-
在链接阶段先将库文件拷贝到build文件夹,在进行链接(链接阶段需要)