本节我们研究大型项目的编译。大型项目的结构如下:
大型项目会划分不同的模块,不同的模块负责不同的功能。在模块文件夹中还有更深一层的文件夹管理,如上图中的module模块中的样子。
每个模块都有一个makefile,负责当前模块的编译工作。
最顶层的makefile负责整体控制。
build文件夹负责存储编译结果,这样可以防止污染源代码。
build文件夹中也有相应的管理结构,和每个模块相关。和project文件夹中的一一对应。
每个模块中的.o文件统一打包成.a文件放到build文件夹根目录下,最终链接.a文件。
模块之间有互相依赖关系,因此,模块需要将功能函数提供出去,每个模块的功能可以通过common模块下的include文件夹提供出去(通过头文件的方式)。
需要的编译环境需求如下:
具体实现:
第一阶段:每个模块编译成一个.a打包文件。
第二阶段:将模块静态库链接在一起生成可执行文件。
任务:
模块makefile的构成:
include部分:用来自动生成依赖关系
all(真正的编译部分):链接静态库文件
实验代码目录如下:
小实验:
当前工作目录在common文件夹下,makefile也在common文件夹下:
在本节中,这个makefile暂时不是顶层makefile,而是模块makefile,将这个makefile分别放到common、module、main文件夹下分别编译:
1 .PHONY : all 2 3 DIR_BUILD := /home/delphi/make/build 4 DIR_COMMON_INC := /home/delphi/make/common/inc 5 6 DIR_SRC := src 7 DIR_INC := inc 8 9 TYPE_INC := .h 10 TYPE_SRC := .c 11 TYPE_OBJ := .o 12 TYPE_DEP := .dep 13 14 AR := ar 15 ARFLAGS := crs 16 17 CC := gcc 18 CFLAGS := -I$(DIR_INC) -I$(DIR_COMMON_INC) 19 20 ifeq ($(DEBUG),true) 21 CFLAGS += -g 22 endif 23 24 MODULE := $(realpath .) 25 MODULE := $(notdir $(MODULE)) 26 DIR_OUTPUT := $(addprefix $(DIR_BUILD)/, $(MODULE)) 27 28 OUTPUT := $(MODULE).a 29 OUTPUT := $(addprefix $(DIR_BUILD)/, $(OUTPUT)) 30 31 SRCS := $(wildcard $(DIR_SRC)/*$(TYPE_SRC)) 32 OBJS := $(SRCS:$(TYPE_SRC)=$(TYPE_OBJ)) 33 OBJS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(OBJS)) 34 DEPS := $(SRCS:$(TYPE_SRC)=$(TYPE_DEP)) 35 DEPS := $(patsubst $(DIR_SRC)/%, $(DIR_OUTPUT)/%, $(DEPS)) 36 37 vpath %$(TYPE_INC) $(DIR_INC) 38 vpath %$(TYPE_INC) $(DIR_COMMON_INC) 39 vpath %$(TYPE_SRC) $(DIR_SRC) 40 41 -include $(DEPS) 42 43 all : $(OUTPUT) 44 @echo "Success! Target ==> $(OUTPUT)" 45 46 $(OUTPUT) : $(OBJS) 47 $(AR) $(ARFLAGS) $@ $^ 48 49 $(DIR_OUTPUT)/%$(TYPE_OBJ) : %$(TYPE_SRC) 50 $(CC) $(CFLAGS) -o $@ -c $(filter %$(TYPE_SRC), $^) 51 52 53 $(DIR_OUTPUT)/%$(TYPE_DEP) : %$(TYPE_SRC) 54 @echo "Creating $@ ..." 55 @set -e; 56 $(CC) $(CFLAGS) -MM -E $(filter %$(TYPE_SRC), $^) | sed 's,(.*).o[ :]*,$(DIR_OUTPUT)/1$(TYPE_OBJ) $@ : ,g' > $@