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

标签 c++ windows visual-c++ visual-studio-2017 precompiled-headers

我有一个当前不使用预编译头的项目,但是我想这样做,因为我已经证明它可以真正提高项目的编译速度。

我还想使用/Zi,因此我可以利用/Zf所暗示的与/Zi关联的并行构建优势。

我使用的是VS2017 C ++编译器,但使用的不是Visual Studio,因此有关配置VS的答案无济于事。

我发现的是,我可以将构建设置为可以使用预编译的头文件,或者可以将其设置为使用/Zi,但是我似乎无法形成一系列适当的调用来完成这两个任务。当我尝试做我认为正确的事情时,最终会出现错误C2958停止构建,并且看不到我做错了什么。

我已经建立了一个玩具项目来演示我所看到的。 pch.hpp标头如下所示:

#pragma once
#include <vector>


我们在main.cpp中做一个简短的主线:

int main() {
    std::vector<int> xs = { 1, 2, 3 };
    return xs.size();
}


请注意,这是main.cpp的完整文件内容:我没有省略任何内容。我故意不在此处包含pch.hpp,因为稍后我们将强制使用/Fi注入它。真正的项目在所有正确的位置都没有包含用于预编译标头的行,并且要进行更新需要成千上万个文件。请注意,使用/Fi的方法确实可以在下面的命令行中使用,并且具有基于forceincludes的机制也可以与GCC样式的预编译头一起使用的优点。

如果我们在没有/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


我们在构建目录下找到了我们希望找到的文件:

main.obj  pch.obj  pch.pch


但是,如果我们尝试使用/Fi/Fd生成每个文件.pdb并控制其名称,则根本不起作用:

我们可以这样编译预编译的头文件:

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


到目前为止,在build目录中一切正常:

pch.obj  pch.pch  pch.pch.pdb


但是,当我们尝试为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.


这是一个非常令人费解的错误,因为错误消息表明将main.obj.pdb以某种方式视为输入,但实际上,它是根据<< cc>标志,用于构建.pdb

Googling并没有带来太多有用的指导,并且提供了许多有关重新配置VS设置的提示,对于由其他因素驱动的构建而言,这并不是真正有用的。

我还验证了我对/Fdmain.obj的欺骗不是问题。如果将/Fi更新为pch.hpp并从main.cpp的编译行中删除#include pch.hpp,则仍会发生相同的/Fipch.hpp错误。

我意识到使用预编译的头文件时有很多标志需要正确处理,其中包括main.objC2859/Yc标志,但是我认为我已经正确处理了那些标志。

有人看到我错了吗?

编辑1-演示/ Fd可以命名不同的PDB文件

对于下面的评论,似乎并非所有目标都必须共享/Yu参数。这是一个不使用预编译头的示例,该示例演示了这一点:

在这里,我需要添加/Fp并创建同时链接它的/Fdcommon.cpp

main1.cpp标头看起来像:

#pragma once

int common(int arg);


main2.cpp中实现一个简单的实现:

#include "common.hpp"

int common(int arg) {
    return arg + 42;
}


然后介绍两个不同的主线:

common.hpp

#include "common.hpp"

int main() {
    std::vector<int> xs = { 1, 2, 3 };
    return common(xs.size());
}


common.cpp

#include "common.hpp"

int main() {
    std::vector<int> xs = { 1, 2, 3, 4};
    return common(xs.size());
}


然后编译所有三个目标文件。请注意,这里仍然有main1.cpp来使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


我们在main2.cpp目录中找到了期望的结果:

common.obj  common.obj.pdb  main1.obj  main1.obj.pdb  main2.obj  main2.obj.pdb


然后,我们可以继续进行链接:

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


我们发现在/Fipch.hpp目录中同时获得了可执行文件和这些可执行文件的PDB文件:

common.obj      main1.exe  main1.obj.pdb  main2.exe  main2.obj.pdb
common.obj.pdb  main1.obj  main1.pdb      main2.obj  main2.pdb

最佳答案

从Microsoft C2859creating precompiled headers文档中,必须使用预编译头在所有源模块之间共享用于存储调试信息的数据库。将所有共享的调试信息存储在一个数据库中,可大大减少目标文件的大小并加快链接速度,因为链接器不必处理所有重复操作。

如果希望所有编译文件都具有自己的调试信息副本,请使用/Z7选项。

关于c++ - 结合使用PCH,PDB和Zi会导致VS2017令人困惑的C2859编译错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55447079/

相关文章:

c++ - 哪个函数更快 push_back,在 C++ 中插入

C++/Qt - 无法从 qrc 文件打开 .txt

c++ - 完成 vector 后是否需要调用 clear() ?

c++ - 如何在 Microsoft Visual Studio 2013 中的同一解决方案中执行不同的 C++ 文件?

c++ - 如何使用 C++ 读取 sqlite 数据。?

mysql - Visual C++/MySql 应用程序在其他计算机上被锁定

c - 通过函数调用扩展 C 中局部变量的范围

windows - 在什么情况下 GetMsgProc 函数会收到小于 0 的代码?

安卓CDT : Possible to use ndk-build without bash (Eclipse in Windows)?

visual-c++ - VC++ 中的默认模板参数