目录
1、为什么要将 .h 编译生成 .gch 文件
gch 文件,也称为预编译头文件(Precompiled Header,简称PCH),当多个源文件(如.cpp文件)包含同一个头文件(如.h文件)时,传统的编译方式会在每个源文件中都预编译这个头文件,这会导致大量的重复工作,从而浪费编译时间。
为了解决这个问题,可以使用预编译头文件技术。预编译头文件将头文件预编译为二进制代码(即GCH文件),然后在后续的编译过程中,编译器可以直接使用这个二进制代码,而不需要再次解析头文件。这样可以大大减少编译时间。
2、工程示例
这里使用一个简单的工程来将不同目录下的 .h 文件编译生成 .gch 文件,并保存到 build目录下。当前测试工程下 common 和 util 目录包含 .h 或 .hpp 文件。

3、Makefile
(1)获取头文件
ROOTDIR = .
BUILD_DIR = build
# 获取 common 和 util 下的所有头文件
C_INCLUDES = $(wildcard $(ROOTDIR)/common/*.h $(ROOTDIR)/common/*.hpp)
C_INCLUDES += $(wildcard $(ROOTDIR)/util/*.h $(ROOTDIR)/util/*.hpp)
(2)引入头文件目录
头文件之间可能会相互包含,为了避免某个头文件找不到其他头文件,需要在编译时引入所有的头文件目录。
CFLAGS = \
-I$(ROOTDIR)/common \
-I$(ROOTDIR)/util \
(3)设置目标文件的保存位置
C_INCLUDES 变量已经获取到了所有的头文件名称(包含路径),现在要将头文件保存到 build 目录下。
# 将 .h 后缀替换成 .gch,并加上前缀 build/,新的文件名保存到 C_PCHS_TMP
C_PCHS_TMP = $(addprefix $(BUILD_DIR)/,$(notdir $(C_INCLUDES:.h=.gch)))
C_PCHS_TMP += $(addprefix $(BUILD_DIR)/,$(notdir $(C_INCLUDES:.hpp=.gch)))
# 避免 C_PCHS_TMP 中混入 .h 或 .hpp 文件
C_PCHS = $(filter-out %.h %.hpp,$(C_PCHS_TMP))
# 后续使用 %.gch:%.h 进行编译时,方便编译器找到 .h 文件
vpath %.h $(sort $(dir $(C_INCLUDES)))
vpath %.hpp $(sort $(dir $(C_INCLUDES)))
(4)开始编译 .h 和 .hpp 文件
all: $(C_PCHS)
$(BUILD_DIR)/%.gch: %.h
$(CC) -x c++-header $< -o $@
$(BUILD_DIR)/%.gch: %.hpp
$(CC) -x c++-header $< -o $@
4、脚本测试
在终端输入 make 开始编译

build 目录下会生成 .gch 文件

5、完整脚本
ROOTDIR = .
BUILD_DIR = build
C_INCLUDES = $(wildcard $(ROOTDIR)/common/*.h $(ROOTDIR)/common/*.hpp)
C_INCLUDES += $(wildcard $(ROOTDIR)/util/*.h $(ROOTDIR)/util/*.hpp)
CFLAGS = \
-I$(ROOTDIR)/common \
-I$(ROOTDIR)/util \
C_PCHS_TMP = $(addprefix $(BUILD_DIR)/,$(notdir $(C_INCLUDES:.h=.gch)))
C_PCHS_TMP += $(addprefix $(BUILD_DIR)/,$(notdir $(C_INCLUDES:.hpp=.gch)))
C_PCHS = $(filter-out %.h %.hpp,$(C_PCHS_TMP))
vpath %.h $(sort $(dir $(C_INCLUDES)))
vpath %.hpp $(sort $(dir $(C_INCLUDES)))
all: $(C_PCHS)
$(BUILD_DIR)/%.gch: %.h
$(CC) -x c++-header $< -o $@
$(BUILD_DIR)/%.gch: %.hpp
$(CC) -x c++-header $< -o $@
.PHONY:clean
clean:
rm -rf $(BUILD_DIR)/*
5948

被折叠的 条评论
为什么被折叠?



