[英]Combining PCH, PDB, and Zi leads to puzzling C2859 compile error with VS2017
I have a project that doesn't currently use precompiled headers, but I'd like to make it do so, as I've demonstrated that it leads to real compilation speed improvements in the project. 我有一个当前不使用预编译头的项目,但是我想这样做,因为我已经证明它可以真正提高项目的编译速度。
I'd also like to use /Zi
, so I can capitalize on the parallel build benefits associated with /Zf
, which is implied by /Zi
. 我也想用/Zi
,这样我就可以与相关的并行构建利益利用/Zf
,这是隐含/Zi
。
I'm using the VS2017 C++ compiler, but I'm using a build system that is not Visual Studio, so answers relating to configuring VS aren't helpful. 我使用的是VS2017 C ++编译器,但使用的不是Visual Studio,因此有关配置VS的答案无济于事。
What I'm finding is that I can set up the build to use precompiled headers just fine, or I can set it up to use /Zi
just fine, but I can't seem to form a proper series of invocations to do both. 我发现的是,我可以将构建设置为可以使用预编译的头文件,或者可以将其设置为使用/Zi
很好,但是我似乎无法形成适当的一系列调用来完成这两项工作。 When I try to do what I think is correct, I end up with error C2958
stopping the build, and I don't see what I'm doing wrong. 当我尝试执行我认为正确的操作时,最终会收到错误C2958
停止构建,并且看不到我做错了什么。
I've built a toy project to demonstrate what I'm seeing. 我已经建立了一个玩具项目来演示我所看到的。 The pch.hpp
header looks like this: pch.hpp
标头看起来像这样:
#pragma once
#include <vector>
And we make a short mainline in main.cpp
: 我们在main.cpp
做一个简短的主线:
int main() {
std::vector<int> xs = { 1, 2, 3 };
return xs.size();
}
Note that this is the complete file contents for main.cpp
: I haven't omitted anything. 请注意,这是main.cpp
的完整文件内容:我没有省略任何内容。 I am intentionally not including pch.hpp
here, because we are going to force inject it with /Fi
later. 我故意不在此处包含pch.hpp
,因为pch.hpp
我们将强制使用/Fi
注入它。 The real project doesn't have include lines for a precompiled header in all the right places, and there would be thousands of files to update to do so. 真正的项目在所有正确的位置都没有包含用于预编译标头的行,并且要进行更新需要成千上万个文件。 Note that the approach using /Fi
does appear to work in the command lines below, and has the advantage that a forceincludes based mechanism can work with GCC style precompiled headers as well. 请注意,使用/Fi
的方法确实可以在下面的命令行中使用,并且具有基于forceincludes的机制也可以与GCC样式的预编译头一起使用的优点。
If we build things without /Zi
, everything goes fine: 如果我们在没有/Zi
情况下构建东西,那么一切都会很好:
cl /Fobuild\pch.obj /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /c pch.hpp /Yc /Fpbuild\pch.pch
pch.hpp
cl /Fobuild\main.obj /c main.cpp /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /FIpch.hpp /Yupch.hpp /Fpbuild/pch.pch
main.cpp
We find the files we would expect to find under the build directory: 我们在构建目录下找到了我们希望找到的文件:
main.obj pch.obj pch.pch
However, if we try to use /Fi
and /Fd
to generate a per-file .pdb
and control its name, it doesn't work at all: 但是,如果我们尝试使用/Fi
和/Fd
生成每个文件.pdb
并控制其名称,则根本无法使用:
We can compile the precompiled header that way OK: 我们可以这样编译预编译的头文件:
cl /Fobuild\pch.obj /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /c pch.hpp /Yc /Fpbuild\pch.pch /Zi /Fdbuild\pch.pch.pdb
And things look OK in the build
directory so far: 到目前为止,在build
目录中一切正常:
pch.obj pch.pch pch.pch.pdb
But when we try to build the object file for main, it all falls apart: 但是,当我们尝试为main构建目标文件时,一切都崩溃了:
cl /Fobuild\main.obj /c main.cpp /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /FIpch.hpp /Yupch.hpp /Fpbuild/pch.pch /Zi /Fdbuild\main.obj.pdb
main.cpp
main.cpp: error C2859: Z:\data\acm\src\example\build\main.obj.pdb is not the pdb file that was used when this precompiled header was created, recreate the precompiled header.
This is a very puzzling error, because the error message suggests that main.obj.pdb
is being treated as an input somehow, but in fact it is intended to be the name of the .pdb
file generated as output , per the value of the /Fd
flag for the build of main.obj
. 这是一个非常令人费解的错误,因为错误消息表明将main.obj.pdb
以某种方式视为输入 ,但实际上,它的意图是将其作为输出生成的.pdb
文件的名称,具体取决于/Fd
标志,用于生成main.obj
。
Googling hasn't resulted in much useful guidance, with a lot of hints about reconfiguring VS settings, which isn't really useful in the case of a build driven by something else. Googling并没有带来太多有用的指导,并且提供了许多有关重新配置VS设置的提示,对于由其他因素驱动的构建而言,这并不是真正有用的。
I've also verified that my trickery with the /Fi
of pch.hpp
isn't the issue. 我还验证了我对pch.hpp
的/Fi
pch.hpp
不是问题。 If I update main.cpp
to #include pch.hpp
and remove the /Fipch.hpp
from the compile line for main.obj
, the same C2859
error still occurs. 如果我将main.cpp
更新为#include pch.hpp
并从/Fipch.hpp
的编译行中main.obj
,则仍然会发生相同的C2859
错误。
I realize there are a lot of flags to get right when using precompiled headers, among them the /Yc
and /Yu
and /Fp
flags, but I think I've got those handled correctly. 我意识到使用预编译的标头时有很多标志可以正确使用,其中包括/Yc
和/Yu
和/Fp
标志,但是我认为我已经正确处理了那些标志。
Does anyone see where I've got things wrong? 有人看到我错了吗?
Edit 1 - Demonstrating that /Fd can name different PDB files 编辑1-演示/ Fd可以命名不同的PDB文件
In response to the comment below, it doesn't actually appear to be the case that all targets must share the /Fd
argument. 对于下面的评论,似乎并不是所有目标都必须共享/Fd
参数。 Here is an example without the use of precompiled headers that demonstrates that: 这是一个不使用预编译头的示例,该示例演示了这一点:
Here, I've needed to add common.cpp
and create a main1.cpp
and main2.cpp
which both link it: 在这里,我需要添加common.cpp
并创建一个main1.cpp
和main2.cpp
来链接它们:
The common.hpp
header looks like: common.hpp
标头看起来像:
#pragma once
int common(int arg);
With a trivial implementation in common.cpp
: 在common.cpp
有一个简单的实现:
#include "common.hpp"
int common(int arg) {
return arg + 42;
}
Then introduce two different mainlines: 然后介绍两个不同的主线:
main1.cpp
: main1.cpp
:
#include "common.hpp"
int main() {
std::vector<int> xs = { 1, 2, 3 };
return common(xs.size());
}
And main2.cpp
: 和main2.cpp
:
#include "common.hpp"
int main() {
std::vector<int> xs = { 1, 2, 3, 4};
return common(xs.size());
}
Then compile all three object files. 然后编译所有三个目标文件。 Note that we still have the /Fipch.hpp
here to make the includes work right, but we aren't using any of the actual PCH machinery: 请注意,这里我们仍然具有/Fipch.hpp
来使include工作正常,但是我们没有使用任何实际的PCH机制:
cl /Fobuild\common.obj /c common.cpp /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /FIpch.hpp /Zi /Fdbuild\common.obj.pdb
cl /Fobuild\main1.obj /c main1.cpp /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /FIpch.hpp /Zi /Fdbuild\main1.obj.pdb
cl /Fobuild\main2.obj /c main2.cpp /TP /nologo /EHsc /errorReport:none /MD /O2 /Oy- /Zc:rvalueCast /Zc:strictStrings /volatile:iso /Zc:__cplusplus /permissive- /std:c++17 /Zc:inline /FIpch.hpp /Zi /Fdbuild\main2.obj.pdb
And we find what we would expect in the build
directory: 我们在build
目录中找到了期望的结果:
common.obj common.obj.pdb main1.obj main1.obj.pdb main2.obj main2.obj.pdb
Then we can go ahead and link: 然后,我们可以继续进行链接:
link /nologo /DEBUG /INCREMENTAL:NO /LARGEADDRESSAWARE /OPT:REF /OUT:build\main1.exe /PDB:build\main1.pdb build\common.obj build\main1.obj
link /nologo /DEBUG /INCREMENTAL:NO /LARGEADDRESSAWARE /OPT:REF /OUT:build\main2.exe /PDB:build\main2.pdb build\common.obj build\main2.obj
And we find that we get both executables and PDB files for those executables in the build
directory: 我们发现在build
目录中同时获得了可执行文件和这些可执行文件的PDB文件:
common.obj main1.exe main1.obj.pdb main2.exe main2.obj.pdb
common.obj.pdb main1.obj main1.pdb main2.obj main2.pdb
From the Microsoft documentation for C2859 and creating precompiled headers , the database used to store the debug information must be shared among all source modules using the precompiled header. 从Microsoft文档C2859和创建预编译的头文件开始 ,用于存储调试信息的数据库必须使用预编译的头文件在所有源模块之间共享。 Having all that shared debug information stored in one database greatly reduces the size of the object files and speeds up linking since the linker does not have to deal with all that repetition. 将所有共享的调试信息存储在一个数据库中,可大大减少目标文件的大小,并加快链接速度,因为链接器不必处理所有重复操作。
If you want all your compiled files to have their own copies of the debug information, use the /Z7
option instead. 如果希望所有编译文件都拥有自己的调试信息副本,请改用/Z7
选项。
声明:本站的技术帖子网页,遵循CC BY-SA 4.0协议,如果您需要转载,请注明本站网址或者原文地址。任何问题请咨询:yoyou2525@163.com.