在 MS Visual C 上链接到 protobuf 3 时出错
Errors when linking to protobuf 3 on MS Visual C
遇到于 Visual Studio 2013,但它可以在任何版本中重现。
我从github, 运行 CMake-gui克隆了protocol buffer库(我把所有东西都保留为默认值,所以它是静态版本),只构建了libprotobuf(其他项目由于某种原因失败,cmd.exe 错误,可能与测试有关,但 libprotobuf 构建良好)。
我的项目使用 headers 由 mapbox 矢量切片规范 github 中的 .proto 文件生成。
当我link时,我第一次遇到这个错误
Error 1 error C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators' s:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility
我尝试在其他命令行参数中使用 -D_SCL_SECURE_NO_WARNINGS
禁用它,但随后出现其他错误:
Error 1 error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MTd_StaticDebug' doesn't match value 'MDd_DynamicDebug' in main.obj S:\eiogit3\misc-projs\mapload\mapload\libprotobufd.lib(common.obj)
这与 VStudio C(和 C++)运行时库(VCRTLib 或 UCRT - 检查 [SO]: How to circumvent Windows Universal CRT headers dependency on vcruntime.h (@CristiFati's answer)) 被你的项目和 libprotobuf项目。让我详细说说:
假设有一些 C (C++) 代码。该代码的目的是 运行。比可以达到:
- 直接:将该代码包含在 VC Application 类型的项目中 - 这将生成一个 .exe
- 间接:将代码包含在 VC Library 类型的项目中——这将生成一个 library,它只会从另一个 .exe(调用该库)调用时能够 运行。该库可以是:
- static:所有的C(C++)代码都会被编译存储在一个 .lib 文件。在另一个项目(无论是应用程序还是库)中使用该库时,您将需要该文件 - 在 link 时间。请注意,您的 .lib 中所有需要的代码都将“复制”到另一个项目中
- dynamic:你现在将有 2 个文件:一个 .dll 文件,它将包含已编译的(和 link ed) 代码,和一个 .lib 文件(1) 将包含指向 .dll 文件中代码的“指针”(如果您愿意的话)。在另一个项目中使用该库时,在link时还需要.lib文件,但现在不会了包含代码,因此它不会被复制到另一个库中(另一个库会更小),但在 运行 时间另一个库将需要 .dll 文件
您可以检查 for details of how C (C++) code gets to be transformed in executable format. Also Google is full of articles about differences between static and dynamic libraries, when to use one or the other, an example can be found on [SO]: When to use dynamic vs. static libraries。
如您所料,CRT 或 C RunTime 库(包含底层使 C 代码能够 运行 的系统 - 一个例子是内存管理功能:malloc、free) 也不例外 - 它相当于 Nix 的 libc.a(静态或存档)与 libc.so(动态或共享对象) - 但在 VStudio 中有点复杂:
- 静态 CRT 驻留在 libcmt.lib
- 动态 CRT 驻留在 msvcrt.lib 中,它“指向”msvcr###.dll (2) (msvcr120.dll 对于 VStudio 2013)
备注:
- 库名末尾的“d”(msvcrd.lib), 意味着它是用调试符号编译的
- C++ 运行时间库在确切的情况下;名称有一个额外的 p:libcpmt.lib, msvcprt.lib, msvcp120.dll
- 更多详情,请查看[MS.Docs]: CRT Library Features
现在,UCRT 部分不像任何其他库一样包含在项目中(项目属性 -> 链接器 -> 输入 -> 附加依赖项),但因为它们的性质(静态或动态)在编译时是必需的,所以它们的配置来自:[MS.Docs]: /MD, /MT, /LD (Use Run-Time Library),其中有 4 个可用选项:
- 多线程(/MT)
- 多线程调试(/MTd)
- 多线程DLL(/MD)
- 多线程调试 DLL (/MDd)
显然,包含“Debug”的那些是为 Debug 配置构建的,而其他包含 Release 的;关键是那些有 DLL 的使用的是 dynamic 运行time 版本,而其他的是 静态版本。
回到你的错误:linker 抱怨 main.obj(你项目的一部分)有MDd_DynamicDebug(link针对动态调试版本),而 common.obj(libprotobuf 项目的一部分)有 MTd_StaticDebug(link针对 static debug 版本),所以你 link 针对 2 运行 次可执行文件(或 .dll)- 这是不可能的。
为了修复它,您应该确保 libprotobuf 和您的主项目具有相同的 UCRT 值。
当然,更改主项目设置以匹配 libprotobuf 的设置更简单,但建议使用动态 运行time 版本(事情会变得混乱在涉及 .dlls 的较大项目中)即使这需要重新编译 libprotobuf(好吧,如果更改该选项会产生错误,使 libprotobuf 很难构建,而您的项目将保持如此简单,您可以使用静态 UCRT).
注意:不要将UCRT类型(静态/动态)误认为是libprotobuf 正在构建(此时是静态的,但我确信它也可以构建为动态的)。
更新#0
根据一些评论的要求,在上面的说明中添加一些额外的信息,它可能对其他用户有用。
关于一个库(包括 libprotobuf)有两个方面完全不相关:
- 库类型(构建方式):dynamic / static
- UCRT类型(它使用UCRT的方式):再次,动态 / 静态
所以,有 4 个完全有效的组合:
- 动态库使用动态UCRT
- 使用静态的动态库UCRT
- 静态库使用动态UCRT
- 静态库使用static UCRT
对于libprotobuf,每个方面都由布尔cmake选项控制:
- 库类型:protobuf_BUILD_SHARED_LIBS
- UCRT 类型:protobuf_MSVC_STATIC_RUNTIME
这 2 个标志可以通过以下任一方式设置:
- cmake-gui
- cmake cmdline(将它们作为参数传递 - 例如:
-Dprotobuf_BUILD_SHARED_LIBS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF
)
以上4种组合因此可能(至少在v3.5),但 #2. 默认禁用 (指定 -Dprotobuf_BUILD_SHARED_LIBS=ON -Dprotobuf_MSVC_STATIC_RUNTIME=ON
将构建 .dll 将 link 到 dynamic UCRT), 以避免可能的 运行时间问题,启用它需要人工干预。
有关构建说明的更多详细信息(通过 cmake),请查看:[GitHub]: protocolbuffers/protobuf - (master) protobuf/cmake/README.md.
脚注
#1: .lib 文件将只如果库 导出符号 ,则创建它,否则它就没有意义(在 link 时不需要,而 .dll将被创建,但几乎无法使用)
#2:对于较新的 VStudio 版本(以v2015), msvcr(t)部分有被 vc运行time 取代(或者至少这是入口点,因为它被分成更小的逻辑部分(检查URL开头))
遇到于 Visual Studio 2013,但它可以在任何版本中重现。
我从github, 运行 CMake-gui克隆了protocol buffer库(我把所有东西都保留为默认值,所以它是静态版本),只构建了libprotobuf(其他项目由于某种原因失败,cmd.exe 错误,可能与测试有关,但 libprotobuf 构建良好)。
我的项目使用 headers 由 mapbox 矢量切片规范 github 中的 .proto 文件生成。
当我link时,我第一次遇到这个错误
Error 1 error C4996: 'std::_Copy_impl': Function call with parameters that may be unsafe - this call relies on the caller to check that the passed values are correct. To disable this warning, use -D_SCL_SECURE_NO_WARNINGS. See documentation on how to use Visual C++ 'Checked Iterators' s:\program files (x86)\microsoft visual studio 12.0\vc\include\xutility
我尝试在其他命令行参数中使用 -D_SCL_SECURE_NO_WARNINGS
禁用它,但随后出现其他错误:
Error 1 error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MTd_StaticDebug' doesn't match value 'MDd_DynamicDebug' in main.obj S:\eiogit3\misc-projs\mapload\mapload\libprotobufd.lib(common.obj)
这与 VStudio C(和 C++)运行时库(VCRTLib 或 UCRT - 检查 [SO]: How to circumvent Windows Universal CRT headers dependency on vcruntime.h (@CristiFati's answer)) 被你的项目和 libprotobuf项目。让我详细说说:
假设有一些 C (C++) 代码。该代码的目的是 运行。比可以达到:
- 直接:将该代码包含在 VC Application 类型的项目中 - 这将生成一个 .exe
- 间接:将代码包含在 VC Library 类型的项目中——这将生成一个 library,它只会从另一个 .exe(调用该库)调用时能够 运行。该库可以是:
- static:所有的C(C++)代码都会被编译存储在一个 .lib 文件。在另一个项目(无论是应用程序还是库)中使用该库时,您将需要该文件 - 在 link 时间。请注意,您的 .lib 中所有需要的代码都将“复制”到另一个项目中
- dynamic:你现在将有 2 个文件:一个 .dll 文件,它将包含已编译的(和 link ed) 代码,和一个 .lib 文件(1) 将包含指向 .dll 文件中代码的“指针”(如果您愿意的话)。在另一个项目中使用该库时,在link时还需要.lib文件,但现在不会了包含代码,因此它不会被复制到另一个库中(另一个库会更小),但在 运行 时间另一个库将需要 .dll 文件
您可以检查
如您所料,CRT 或 C RunTime 库(包含底层使 C 代码能够 运行 的系统 - 一个例子是内存管理功能:malloc、free) 也不例外 - 它相当于 Nix 的 libc.a(静态或存档)与 libc.so(动态或共享对象) - 但在 VStudio 中有点复杂:
- 静态 CRT 驻留在 libcmt.lib
- 动态 CRT 驻留在 msvcrt.lib 中,它“指向”msvcr###.dll (2) (msvcr120.dll 对于 VStudio 2013)
备注:
- 库名末尾的“d”(msvcrd.lib), 意味着它是用调试符号编译的
- C++ 运行时间库在确切的情况下;名称有一个额外的 p:libcpmt.lib, msvcprt.lib, msvcp120.dll
- 更多详情,请查看[MS.Docs]: CRT Library Features
现在,UCRT 部分不像任何其他库一样包含在项目中(项目属性 -> 链接器 -> 输入 -> 附加依赖项),但因为它们的性质(静态或动态)在编译时是必需的,所以它们的配置来自:[MS.Docs]: /MD, /MT, /LD (Use Run-Time Library),其中有 4 个可用选项:
- 多线程(/MT)
- 多线程调试(/MTd)
- 多线程DLL(/MD)
- 多线程调试 DLL (/MDd)
显然,包含“Debug”的那些是为 Debug 配置构建的,而其他包含 Release 的;关键是那些有 DLL 的使用的是 dynamic 运行time 版本,而其他的是 静态版本。
回到你的错误:linker 抱怨 main.obj(你项目的一部分)有MDd_DynamicDebug(link针对动态调试版本),而 common.obj(libprotobuf 项目的一部分)有 MTd_StaticDebug(link针对 static debug 版本),所以你 link 针对 2 运行 次可执行文件(或 .dll)- 这是不可能的。
为了修复它,您应该确保 libprotobuf 和您的主项目具有相同的 UCRT 值。
当然,更改主项目设置以匹配 libprotobuf 的设置更简单,但建议使用动态 运行time 版本(事情会变得混乱在涉及 .dlls 的较大项目中)即使这需要重新编译 libprotobuf(好吧,如果更改该选项会产生错误,使 libprotobuf 很难构建,而您的项目将保持如此简单,您可以使用静态 UCRT).
注意:不要将UCRT类型(静态/动态)误认为是libprotobuf 正在构建(此时是静态的,但我确信它也可以构建为动态的)。
更新#0
根据一些评论的要求,在上面的说明中添加一些额外的信息,它可能对其他用户有用。
关于一个库(包括 libprotobuf)有两个方面完全不相关:
- 库类型(构建方式):dynamic / static
- UCRT类型(它使用UCRT的方式):再次,动态 / 静态
所以,有 4 个完全有效的组合:
- 动态库使用动态UCRT
- 使用静态的动态库UCRT
- 静态库使用动态UCRT
- 静态库使用static UCRT
对于libprotobuf,每个方面都由布尔cmake选项控制:
- 库类型:protobuf_BUILD_SHARED_LIBS
- UCRT 类型:protobuf_MSVC_STATIC_RUNTIME
这 2 个标志可以通过以下任一方式设置:
- cmake-gui
- cmake cmdline(将它们作为参数传递 - 例如:
-Dprotobuf_BUILD_SHARED_LIBS=OFF -Dprotobuf_MSVC_STATIC_RUNTIME=OFF
)
以上4种组合因此可能(至少在v3.5),但 #2. 默认禁用 (指定 -Dprotobuf_BUILD_SHARED_LIBS=ON -Dprotobuf_MSVC_STATIC_RUNTIME=ON
将构建 .dll 将 link 到 dynamic UCRT), 以避免可能的 运行时间问题,启用它需要人工干预。
有关构建说明的更多详细信息(通过 cmake),请查看:[GitHub]: protocolbuffers/protobuf - (master) protobuf/cmake/README.md.
脚注
#1: .lib 文件将只如果库 导出符号 ,则创建它,否则它就没有意义(在 link 时不需要,而 .dll将被创建,但几乎无法使用)
#2:对于较新的 VStudio 版本(以v2015), msvcr(t)部分有被 vc运行time 取代(或者至少这是入口点,因为它被分成更小的逻辑部分(检查URL开头))