c++ - GCC - 多个预编译头文件和特定路径

标签 c++ linux gcc makefile precompiled-headers

背景


我有一个大型的 Makefile 项目正在处理,我想稍微整理一下。它构建了几十个子项目,每个子项目包含大约 100 个 .cpp.h 文件。我已经对其进行了设置,以便它能够为多个操作系统(Linux、OSX/Mac、QNX 等)和多个体系结构构建debugrelease 构建( x86/i386, x64/amd64, armhf, arm64/aarch64) 并行。这是因为它是一个庞大的项目,而使其快速构建的唯一方法是并行使用多个工具链。

我有一个所有项目都遵守的主要规则,即在构建时将中间对象(即:.o 文件)存储在临时目录中。因此,为 Linux、arm64、 Release模式构建 test.c;将在当前工作目录的以下子目录中构建目标文件:

.tmp/Linux/arm64/release

问题


此功能在我的构建中没有问题,但使用此设置,我似乎无法在 GCC 中正确使用预编译 header (即:.GCH 文件)。在我的设置中,我有一个 stdafx.h/stdafx.cpp 对。使用 GCC,我可以很容易地创建一个 stdafx.h.gch 文件。但是,如果文件与源文件位于同一路径,该项目似乎只使用它(加速构建)。如果预编译 header 位于中间对象路径(即:.tmp/Linux/arm64/release),则它不会被检测到或使用。即使我明确地将包含路径添加到包含 gch 文件的中间对象路径,它也会失败。包括文件名本身的完整路径会导致它被视为无效的链接描述文件,并被忽略。

因此,我的第一个解决方法是制定一条规则,强制所有 OS/arch 构建等待初始预编译 header 生成,而不是在每个 OS/arch 基础上构建 gch .但是,如果我使用 Release模式设置构建 gch 并尝试制作 调试版本,我会收到以下警告:

warning: stdafx.h.gch: created with -gnone, but used with -gdwarf-2

首先,我不知道这是否会对我的构 build 成严重后果,其次,不同的操作系统可能会通过不同的编译时间定义 gch 生成标志,所以这不是据我所知,“一刀切”用例。


问题


我该如何解决这个问题,以便预编译的 header 位于 $PWD 以外的位置并且可以被 GCC 检测到?我目前使用的是 gcc v5.3.1。

谢谢。

最佳答案

这是一个 MVCE对于你的问题 场景:

ma​​in.c

#include <hw.h>
#include <stdio.h>

int main(void)
{
    puts(HW);
    return 0;
}

hw.h

#ifndef HW_H
#define HW_H
#define HW "Hello World"
#endif

生成文件

srcs := main.c
objs := $(addprefix tmp/,$(srcs:.c=.o))
pch := tmp/hw.h.gch
CPPFLAGS += -I. 

.PHONY: all clean

all: hw

tmp:
    mkdir -p tmp

tmp/%.o: %.c | $(pch)
    gcc -c $(CPPFLAGS) -o $@ $<

$(pch): hw.h | tmp
    gcc -c $(CPPFLAGS) -o $@ $<
ifdef ENFORCE_PCH
    echo "#error Debug." >> $^
endif 

hw: $(objs)
    gcc -o $@ $^


clean:
    sed -i '/^#error/d' hw.h
    rm -fr hw tmp

此项目在 tmp 中输出其中间文件. .o文件去那里 PCH也是hw.h.gch .

构建并运行它:

$ make && ./hw
mkdir -p tmp
gcc -c -I.  -o tmp/hw.h.gch hw.h
gcc -c -I.  -o tmp/main.o main.c
gcc -o hw tmp/main.o
Hello World

到目前为止一切顺利。但它真的使用了 PCH 吗?让我们看看:

$ make clean
sed -i '/^#error/d' hw.h
rm -fr hw tmp
$ make ENFORCE_PCH=true
mkdir -p tmp
gcc -c -I.  -o tmp/hw.h.gch hw.h
echo "#error Debug." >> hw.h
gcc -c -I.  -o tmp/main.o main.c
In file included from main.c:1:0:
./hw.h:5:2: error: #error Debug.
 #error Debug.
  ^
Makefile:15: recipe for target 'tmp/main.o' failed
make: *** [tmp/main.o] Error 1

不,它没有。我们知道,因为 ENFORCE_PCH定义,我们有 钉了一个#error指令结束 hw.h 生成 好tmp/hw.h.gch .因此,如果前者随后是 #include -在任何地方 而不是后者,构建中断。它刚刚做了什么。

这是应该的。 GCC 手册 3.21 Using Precompiled Headers , 段。 3:

A precompiled header file is searched for when #include is seen in the compilation. As it searches for the included file (see Search Path) the compiler looks for a precompiled header in each directory just before it looks for the include file in that directory. The name searched for is the name specified in the #include with ‘.gch’ appended. If the precompiled header file can't be used, it is ignored.

因此,给定包含搜索路径 . , 指令 #include <hw.h>会引发 gcc检查 PCH ./hw.h.gch使用前 ./hw.h , 因为有 没有./hw.h.gch , 它将使用 ./hw.h .

从刚刚引用的文档来看,添加 tmp 可能看起来到包含搜索路径 - CPPFLAGS += -Itmp -I. - 应该导致 tmp/hw.h.gch优先于 ./hw.h 使用.但实际上并没有什么区别。 该文档省略了一个关键条件。第二句应该是:

As it searches for the included file (see Search Path) the compiler looks for a precompiled header in each directory just before it looks for the include file in that directory and will use the precompiled header for preference if the include file is found.

要被发现和使用,PCH 必须是匹配 header 的兄弟。并考虑 这就是你想要的。否则,一个 a/foo.h.gch没有匹配的兄弟标题可能是 感谢 -Ia 找到并使用了当有b/foo.h.gch , 与匹配 b/foo.h , 那 多亏了后来的 -Ib 才能找到并使用.显然,后者是更合理的选择。

有了这种洞察力,不难看出解决方案:如果你真的想编译和使用 不是其源 header 兄弟的 PCH,确保给它一个 phony 匹配 header 一个 sibling 。你可以按照你认为合适的方式安排,例如

Makefile(固定)

srcs := main.c
objs := $(addprefix tmp/,$(srcs:.c=.o))
pch := tmp/hw.h.gch
# Seek headers in tmp first...
CPPFLAGS += -Itmp -I.

.PHONY: all clean

all: hw

tmp:
    mkdir -p tmp

tmp/%.o: %.c | $(pch)
    gcc -c $(CPPFLAGS) -o $@ $<

$(pch): hw.h | tmp
    # Make phony header in tmp...
    echo "#error You should not be here" > $(basename $@)
    gcc -c $(CPPFLAGS) -o $@ $<
ifdef ENFORCE_PCH
    echo "#error Debug." >> $^
endif 

hw: $(objs)
    gcc -o $@ $^

clean:
    sed -i '/^#error/d' hw.h
    rm -fr hw tmp

看到 PCH 是从 tmp 使用的:

$ make clean
sed -i '/^#error/d' hw.h
rm -fr hw tmp
$ make ENFORCE_PCH=true && ./hw
mkdir -p tmp
# Make phony header in tmp...
echo "#error You should not be here" > tmp/hw.h
gcc -c -Itmp -I. -o tmp/hw.h.gch hw.h
echo "#error Debug." >> hw.h
gcc -c -Itmp -I. -o tmp/main.o main.c
gcc -o hw tmp/main.o
Hello World

关于c++ - GCC - 多个预编译头文件和特定路径,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/37215458/

相关文章:

C++ 参数传递数组与单个项

python - 将变量传递给环境

linux - MEM_SHARED、mmap 和硬链接(hard link)

ubuntu - 将 OpenCL.so 库添加到 makefile,我收到错误 "undefined reference to"

c - 是否有用于 linux iptables 的 api ,以便我的程序可以添加防火墙规则

c++ - C++中的线程间通信

c++ - 通过 C++ 从标准输出中的一些打印数据中提取特定值

c - 使用 ‘-Wcast-qual’ 选项检查编译

c++ - P0522R0如何破码?

Linux Alias,它有什么问题?