Warning: file_get_contents(/data/phpspider/zhask/data//catemap/0/windows/15.json): failed to open stream: No such file or directory in /data/phpspider/zhask/libs/function.php on line 167

Warning: Invalid argument supplied for foreach() in /data/phpspider/zhask/libs/tag.function.php on line 1116

Notice: Undefined index: in /data/phpspider/zhask/libs/function.php on line 180

Warning: array_chunk() expects parameter 1 to be array, null given in /data/phpspider/zhask/libs/function.php on line 181
C++ 结合PCH、PDB和Zi会导致令人费解的C2859编译错误与VS2017_C++_Windows_Visual C++_Visual Studio 2017_Precompiled Headers - Fatal编程技术网

C++ 结合PCH、PDB和Zi会导致令人费解的C2859编译错误与VS2017

C++ 结合PCH、PDB和Zi会导致令人费解的C2859编译错误与VS2017,c++,windows,visual-c++,visual-studio-2017,precompiled-headers,C++,Windows,Visual C++,Visual Studio 2017,Precompiled Headers,我有一个项目目前不使用预编译头,但我想让它使用预编译头,因为我已经证明了它可以提高项目的实际编译速度 我还想使用/Zi,因此我可以利用与/Zf相关的并行构建优势,这是/Zi所暗示的 我使用VS2017 C++编译器,但我使用的不是VisualStudio的构建系统,所以与配置VS有关的答案没有帮助。p> 我发现,我可以将构建设置为使用预编译头,也可以将其设置为使用/Zi很好,但我似乎无法形成一系列适当的调用来同时执行这两个操作。当我尝试做我认为正确的事情时,我最终会出现错误C2958停止构建,我

我有一个项目目前不使用预编译头,但我想让它使用预编译头,因为我已经证明了它可以提高项目的实际编译速度

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

我使用VS2017 C++编译器,但我使用的不是VisualStudio的构建系统,所以与配置VS有关的答案没有帮助。p> 我发现,我可以将构建设置为使用预编译头,也可以将其设置为使用
/Zi
很好,但我似乎无法形成一系列适当的调用来同时执行这两个操作。当我尝试做我认为正确的事情时,我最终会出现错误
C2958
停止构建,我看不出我做错了什么

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

#pragma once
#include <vector>
请注意,这是
main.cpp
的完整文件内容:我没有遗漏任何内容。我有意不在这里包括
pch.hpp
,因为我们稍后将强制使用
/Fi
注入它。真正的项目没有在所有正确的位置包含预编译头的include行,因此需要更新数千个文件。请注意,使用
/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
我们可以在build目录下找到我们希望找到的文件:

main.obj  pch.obj  pch.pch
common.obj  common.obj.pdb  main1.obj  main1.obj.pdb  main2.obj  main2.obj.pdb
common.obj      main1.exe  main1.obj.pdb  main2.exe  main2.obj.pdb
common.obj.pdb  main1.obj  main1.pdb      main2.obj  main2.pdb
但是,如果我们尝试使用
/Fi
/Fd
来生成每个文件
.pdb
并控制其名称,则它根本不起作用:

我们可以这样编译预编译头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
到目前为止,在
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
以某种方式被视为一个输入,但事实上,根据构建
main.obj
/Fd
标志的值,它是作为输出生成的
.pdb
文件的名称

谷歌搜索并没有带来太多有用的指导,其中有很多关于重新配置VS设置的提示,这在由其他东西驱动的构建中并不真正有用

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

我意识到在使用预编译头时有很多标志需要正确处理,其中包括
/Yc
/Yu
/Fp
标志,但我认为我已经正确处理了这些标志

有人知道我哪里做错了吗

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

作为对下面评论的回应,实际上似乎并非所有目标都必须共享
/Fd
参数。下面是一个不使用预编译头的示例,该示例演示了:

在这里,我需要添加
common.cpp
并创建一个
main 1.cpp
main2.cpp
两个链接:

common.hpp
标题如下所示:

#pragma once

int common(int arg);
使用
common.cpp
中的一个简单实现:

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

int common(int arg) {
    return arg + 42;
}
#include "common.hpp"

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

int main() {
    std::vector<int> xs = { 1, 2, 3, 4};
    return common(xs.size());
}
然后介绍两条不同的主线:

main1.cpp

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

int common(int arg) {
    return arg + 42;
}
#include "common.hpp"

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

int main() {
    std::vector<int> xs = { 1, 2, 3, 4};
    return common(xs.size());
}
然后编译所有三个对象文件。请注意,我们这里仍然有
/Fipch.hpp
,以使includes正常工作,但我们没有使用任何实际的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
我们可以在
build
目录中找到我们所期望的:

main.obj  pch.obj  pch.pch
common.obj  common.obj.pdb  main1.obj  main1.obj.pdb  main2.obj  main2.obj.pdb
common.obj      main1.exe  main1.obj.pdb  main2.exe  main2.obj.pdb
common.obj.pdb  main1.obj  main1.pdb      main2.obj  main2.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
我们发现,在
build
目录中,我们得到了这些可执行文件的可执行文件和PDB文件:

main.obj  pch.obj  pch.pch
common.obj  common.obj.pdb  main1.obj  main1.obj.pdb  main2.obj  main2.obj.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文档,用于存储调试信息的数据库必须使用预编译头在所有源模块之间共享。将所有共享的调试信息存储在一个数据库中,可以大大减少对象文件的大小并加快链接速度,因为链接器不必处理所有这些重复


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

对于所有编译并链接在一起的文件,/Fd选项必须相同。@HansPassant-我认为在一般情况下实际上不是这样。请参见上面编辑1中详细阐述的示例。至少在PCH机制不起作用的情况下,您实际上可以为每个
cl
调用通过
/Fd
的不同参数创建不同的per TU
.pdb
文件,然后将对象文件链接在一起。试图找到编译器无法报告的方法有什么意义?它只会导致更难诊断的更大问题,例如链接器生成的最终PDB中缺少符号。@HansPassant-使用
/Fi
时,编译
common.obj
应该命名什么PDB文件,鉴于
common.obj
链接到
main1.exe
main2.exe
,请使用lib.exe创建一个静态库,该库可用于将代码链接到不同的可执行文件中。链接器使用与库关联的pdb来挖掘符号。在.obj文件中嵌入符号仍然是可能的,您必须使用/Z7将时钟向后拨三十年