简体   繁体   English

结合使用PCH,PDB和Zi会导致令人困惑的VS2017 C2859编译错误

[英]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.cppmain2.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.

 
粤ICP备18138465号  © 2020-2024 STACKOOM.COM