GCC - 多个预编译头和特定路径

时间:2016-05-13 16:52:23

标签: c++ linux gcc makefile precompiled-headers

背景

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

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

.tmp/Linux/arm64/release

问题

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

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

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

首先,我不知道这对我的构建是否会产生严重后果,其次,不同的操作系统可能会为gch代传递不同的编译时定义标志,因此这不是“一个大小”就我所见,适合所有“用例”。

问题

如何解决这个问题,以便预编译的标头位于$PWD以外的位置,GCC可以检测到它?我目前正在使用gcc v5.3.1。

谢谢。

2 个答案:

答案 0 :(得分:3)

这是您问题的MVCE 情形:

<强>的main.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:

  

在编译中看到#include时,会搜索预编译的头文件。   在搜索包含的文件(请参阅搜索路径)时,编译器会查找   在查找包含文件之前,每个目录中的预编译头   那个目录。搜索到的名称是#include中指定的名称,带有'.gch'   追加。如果无法使用预编译的头文件,则会将其忽略。

因此,在包含搜索路径.的情况下,指令#include <hw.h>将会导致 gcc在使用./hw.h.gch之前检查PCH ./hw.h,并且确实如此 没有./hw.h.gch,它会使用./hw.h

从刚刚引用的文档中可能看出,将tmp添加到包含搜索路径 - CPPFLAGS += -Itmp -I. - 可能会导致 tmp/hw.h.gch优先使用./hw.h。但事实上它没有任何区别。 文档省略了重要的资格。第二句应该是:

  

在搜索包含的文件(请参阅搜索路径)时,编译器会查找   在查找包含文件之前,每个目录中的预编译头   该目录,如果包含文件,将使用预编译头作为首选项   找到了。

要找到并使用,PCH必须是匹配标头的兄弟。并考虑 这就是你想要的。否则,a/foo.h.gch可能没有匹配的兄弟标头 感谢-Ia,当b/foo.h.gch与匹配的b/foo.h匹配时,{ 可以通过以后的-Ib找到并使用。显然,后者是更健全的选择。

有了这种洞察力,不难看出解决方案:如果你真的想编译和使用 PCH不是其源头的兄弟,请务必给它一个 phony 匹配头 那个一个兄弟姐妹。您可以根据需要安排,例如

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

请参阅tmp使用PCH:

$ 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

答案 1 :(得分:0)

如果您将编译器强制设置为-include tmp/pch.h-include-pch tmp/pch.h.gch,则pch.h中的保护块将阻止其再次被包含。

# Makefile
SOURCES := main.cpp
OBJECTS := $(SOURCES:%.cpp=tmp/%.o)
PCH_H := tmp/pch.h
PCH := $(PCH_H).gch

$(PCH) : *.h
    $(COMPILE.cpp) -x c++-header src/pch.h -o $@

$(OBJECTS) : tmp/%.o : src/%.cpp $(PCH)
    $(COMPILE.cpp) -include $(PCH_H)  $< -o $@

注意:

  • gcc搜索名为 pch.h .gch
  • 的预编译标头
  • clang搜索名为 pch.h .pch .gch
  • 的预编译标头
  • clang需要-include pch.h-include-pch pch.h.pch
  • gcc始终搜索预编译的标头,但不支持-include-pch