我有一个当前不使用预编译头的项目,但是我想这样做,因为我已经证明它可以真正提高项目的编译速度。
我还想使用/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设置的提示,对于由其他因素驱动的构建而言,这并不是真正有用的。
我还验证了我对
/Fd
的main.obj
的欺骗不是问题。如果将/Fi
更新为pch.hpp
并从main.cpp
的编译行中删除#include pch.hpp
,则仍会发生相同的/Fipch.hpp
错误。我意识到使用预编译的头文件时有很多标志需要正确处理,其中包括
main.obj
和C2859
和/Yc
标志,但是我认为我已经正确处理了那些标志。有人看到我错了吗?
编辑1-演示/ Fd可以命名不同的PDB文件
对于下面的评论,似乎并非所有目标都必须共享
/Yu
参数。这是一个不使用预编译头的示例,该示例演示了这一点:在这里,我需要添加
/Fp
并创建同时链接它的/Fd
和common.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 C2859和creating precompiled headers文档中,必须使用预编译头在所有源模块之间共享用于存储调试信息的数据库。将所有共享的调试信息存储在一个数据库中,可大大减少目标文件的大小并加快链接速度,因为链接器不必处理所有重复操作。
如果希望所有编译文件都具有自己的调试信息副本,请使用/Z7
选项。
关于c++ - 结合使用PCH,PDB和Zi会导致VS2017令人困惑的C2859编译错误,我们在Stack Overflow上找到一个类似的问题: https://stackoverflow.com/questions/55447079/