文章目录
- makefile文件
- config.mk配置文件
- common.mk文件
目录结构
makefile文件
include config.mk
导入config.mk的文件,通常包含项目的配置变量。
all:@for dir in $(BUILD_DIR); \do \make -C $$dir; \done
all是项目主目标,遍历BUILD_DIR变量每个子目录,并在每个子目录中运行make命令。
$$dir在Shell中被解释为$dir,因为Makefile中的Shell命令需要用 $$来转义$。
-C选项告诉make进入指定目录执行makefile。
clean:rm -rf app/link_obj app/dep nginxrm -rf signal/*.gch app/*.gch
clean目标用于清理文件。
rm -rf命令会强制删除列出的目录和文件,包括编译产生的目标文件、依赖文件、以及.gch预编译头文件。
如果子目录中的Makefile也有all和clean目标,它们应该被设计成可以被递归调用。
config.mk配置文件
定义环境变量,设置项目的编译环境和构建过程。
export BUILD_ROOT = $(shell pwd)
使用pwd
命令动态获取当前工作目录,赋值给BUILD_ROOT
变量。
通过export
,这个变量将对所有被包含的Makefile文件可见,在子目录的Makefile中可以直接引用BUILD_ROOT
。
# 定义头文件的路径
export INCLUDE_PATH = $(BUILD_ROOT)/_include
INCLUDE_PATH
变量指定项目中头文件的存放位置。
这对于编译器来说很重要,因为它需要知道去哪里查找.h
等头文件。
# 定义编译目录
BUILD_DIR = $(BUILD_ROOT)/signal/ \$(BUILD_ROOT)/proc/ \$(BUILD_ROOT)/net/ \$(BUILD_ROOT)/misc/ \$(BUILD_ROOT)/logic/ \$(BUILD_ROOT)/app/
BUILD_DIR
变量列出了需要编译的子目录。
# 编译时是否生成调试信息。GNU调试器可以利用该信息
# 很多调试工具,包括Valgrind工具集都会因为这个为true能够输出更多的调试信息;
export DEBUG = true
DEBUG
变量设为true
意味着编译时将包含调试信息。
这对于开发阶段非常有用,因为生成的可执行文件可以被调试器读取并提供详细的运行时信息。如果设置为false
,则会生成优化过的二进制文件,但调试信息将不会包含在内。
export BUILD_ROOT = $(shell pwd)
export INCLUDE_PATH = $(BUILD_ROOT)/_include
BUILD_DIR = $(BUILD_ROOT)/signal/ \$(BUILD_ROOT)/proc/ \$(BUILD_ROOT)/net/ \$(BUILD_ROOT)/misc/ \$(BUILD_ROOT)/logic/ \$(BUILD_ROOT)/app/
export DEBUG = true
common.mk文件
这段Makefile代码定义了一个更复杂的构建流程,包括条件编译、源文件和目标文件的管理、依赖生成以及清理规则。
.PHONY: all clean
.PHONY
指令确保all
和clean
这两个目标不会被解释器误认为是文件,而是当作伪目标来处理。
ifeq ($(DEBUG),true)
CC = g++ -std=c++11 -g
VERSION = debug
else
CC = g++ -std=c++11
VERSION = release
endif
根据DEBUG
变量的值选择编译器和编译选项。如果是true
,则使用-g
选项生成调试信息。
SRCS = $(wildcard *.cxx)
OBJS = $(SRCS:.cxx=.o)
DEPS = $(SRCS:.cxx=.d)
使用wildcard
函数动态获取当前目录下的所有.cxx
文件,并分别生成对应的.o
和.d
文件列表。
.d
文件是依赖关系文件,修改项目后能决定哪些文件需要重新编译。
LINK_OBJ_DIR = $(BUILD_ROOT)/app/link_obj
DEP_DIR = $(BUILD_ROOT)/app/dep
对象文件和依赖文件的输出目录。
OBJS := $(addprefix $(LINK_OBJ_DIR)/,$(OBJS))
DEPS := $(addprefix $(DEP_DIR)/,$(DEPS))
将.o
和.d
文件的路径前缀修改为相应的输出目录。
$(shell mkdir -p $(LINK_OBJ_DIR))
$(shell mkdir -p $(DEP_DIR))
创建目录,确保输出目录存在。
all: $(DEPS) $(OBJS) $(BIN)
主要构建目标all
依赖于所有.d
文件、.o
文件和最终的二进制文件$(BIN)
。
ifneq ("$(wildcard $(DEPS))","")
include $(DEPS)
endif
如果.d
文件存在,则包含它们,以便Makefile能够识别由.d
文件定义的依赖关系。
$(BIN): $(LINK_OBJ)...$(CC) -o $@ $^ -lpthread
构建最终的可执行文件$(BIN)
,它依赖于所有.o
文件,并链接线程库pthread
。
$(LINK_OBJ_DIR)/%.o: %.cxx$(CC) -I$(INCLUDE_PATH) -o $@ -c $(filter %.cxx,$^)
规则用于编译单个.cxx
源文件到.o
文件,使用-I
选项指定头文件路径。
$(DEP_DIR)/%.d: %.cxxecho -n $(LINK_OBJ_DIR)/ > $@$(CC) -I$(INCLUDE_PATH) -MM $^ >> $@
生成依赖文件.d
,它包含了每个.o
文件对于.cxx
和.h
文件的依赖。-MM
选项生成Makemake风格的依赖文件。
clean:rm -f $(BIN) $(OBJS) $(DEPS) *.gch
清理规则用于删除构建产生的所有文件,包括二进制文件、对象文件、依赖文件和GCC优化文件。
整个Makefile设计了一套完整的自动化构建流程,包括源文件的编译、依赖文件的生成、最终可执行文件的链接以及清理操作。这种结构有助于大型项目的管理和维护。
.PHONY: all clean
ifeq ($(DEBUG),true)CC = g++ -std=c++11 -g
VERSION = debug
else
CC = g++ -std=c++11
VERSION = release
endifSRCS = $(wildcard *.cxx)
OBJS = $(SRCS:.cxx=.o)
DEPS = $(SRCS:.cxx=.d)
BIN := $(addprefix $(BUILD_ROOT)/,$(BIN))
LINK_OBJ_DIR = $(BUILD_ROOT)/app/link_obj
DEP_DIR = $(BUILD_ROOT)/app/dep$(shell mkdir -p $(LINK_OBJ_DIR))
$(shell mkdir -p $(DEP_DIR))OBJS := $(addprefix $(LINK_OBJ_DIR)/,$(OBJS))
DEPS := $(addprefix $(DEP_DIR)/,$(DEPS))LINK_OBJ = $(wildcard $(LINK_OBJ_DIR)/*.o)
LINK_OBJ += $(OBJS)all:$(DEPS) $(OBJS) $(BIN)ifneq ("$(wildcard $(DEPS))","")
include $(DEPS)
endif$(BIN):$(LINK_OBJ)@echo "------------------------build $(VERSION) mode--------------------------------!!!"$(CC) -o $@ $^ -lpthread$(LINK_OBJ_DIR)/%.o:%.cxx$(CC) -I$(INCLUDE_PATH) -o $@ -c $(filter %.cxx,$^)$(DEP_DIR)/%.d:%.cxxecho -n $(LINK_OBJ_DIR)/ > $@$(CC) -I$(INCLUDE_PATH) -MM $^ >> $@