Vcpkg: CMake toolchain debug/release failure

Created on 15 Feb 2017  路  11Comments  路  Source: microsoft/vcpkg

When configuring my own project using CMake, I specify the toolchain file:

cmake -DCMAKE_TOOLCHAIN_FILE=C:/Libs/vcpkg/scripts/buildsystems/vcpkg.cmake ../mySource

All the libraries except boost are found. Manually setting boost libs allows me to generate the project files. I also need to add /DGTEST_LINKED_AS_SHARED_LIBRARY=1 to CMAKE_CXX_FLAGS (as I compile everything as static libraries because of gRPC).

The problem: debug and release versions are all mixed up. Trying to compile project files ends in numerous _ITERATOR_DEBUG_LEVEL mismatches. The errors come from vcpkg-provided libraries protobuf, grpc, gmock (part of gtest).

My project requires the following libraries from vcpkg:

  • boost
  • gRPC
  • protobuf
  • OpenSSL
  • zlib
  • TBB
  • gtest

+some more (not through vcpkg):

  • Qt5
  • ITK
  • Pyhton
  • Doxygen
    etc.

To make the problem more irritating, both gRPC and protobuf provide targets.cmake files (in e.g. C:\Libs\vcpkg\installed\x64-windows-static\share\protobuf). I expect that using these files would solve debug/release library mix-up.

All 11 comments

Hmm, I just built a Boost example and I too had to a set the BOOST_ROOT for CMake to find it properly. The advantage here is that at least vcpkg has uniform installation so setting the ROOT directory was very simple (just the /installed/x86-windows directory for me).

I don't share the rest of your issues but yeah, CMake had trouble finding Boost when I pointed the toolchain file at my vcpkg.cmake file. I haven't tried other installed libraries yet.

But for the vcpkg devs, vcpkg is amazing! Thank you for working on this!

This is part of our project's CMakeLists.txt (perhaps important):

set(BUILD_SHARED_LIBS OFF)
# Configure FindBoost module to locate static libraries
set(Boost_USE_STATIC_LIBS ON)
set(Boost_USE_STATIC_RUNTIME ON)
find_package(Boost 1.53.0 COMPONENTS filesystem program_options REQUIRED)

Thanks for including that snippet!

One thing to note is that you will need to specify -DVCPKG_TARGET_TRIPLET=x86-windows-static if you'd like to use one of the non-base triplets. Because you were able to successfully find the packages, I assume you did that and just dropped it from the command line you noted above.

I'll take a look into the IDL issues; as you've correctly surmised, the CMake config files should have taken care of everything. I'll see if I can reproduce the confusion locally -- if you have any other relevant notes about your build (the full cmake configure line and any modifications you've made to CMAKE_CXX_FLAGS), that could be handy. We only support the CMake "Debug" and "Release" configurations out of the box.

As for Boost: the long story short is that if you remove the lines for Boost_USE_STATIC_LIBS and Boost_USE_STATIC_RUNTIME, your program should link exactly as you want (including getting static libs against the static runtime).

The longer story is that Vcpkg is designed to have the linkage type of each library is specified up front, in the triplet file. In the case of x86-windows we build dynamic/dynamic of everything, and in the case of x86-windows-static we build static libs of everything with the static CRT. Most libraries name their import libraries the same in both of these cases, so it's trivial for consumers to pick up the correct, single library build type (they always link the same .lib name).

However, in the case of Boost, things are more complicated because the build type is encoded into the name -- to enable side-by-side installation of the development files. By default on Windows, Boost relies on its #pragma auto-linking to resolve the different library names, so consumers can avoid having to explicitly encode the Boost compilation settings.

But, when running find_package from CMake (as you've done in your project), Boost _requires_ the consuming project to know and hard-code the linkage type in their CMake file. This ends up being very problematic from the point of view that (within a triplet) we want to build everything exactly once according to the user's specification and link them all together with no duplication.

After considering various options, the point we have currently arrived at is to rename the .lib files to always match the default find_package() options (dynamic/dynamic). The runtime DLLs (if they exist) will still retain their original names, so side-by-side deployment is unaffected. When not using Vcpkg, you can always specify -DBoost_USE_STATIC_LIBS on your CMake command line to specify the boost settings for that particular environment. Alternatively, if you don't mind having Vcpkg-specific lines in your build script, you can use if(NOT VCPKG_TOOLCHAIN) to avoid setting those parameters when not using Vcpkg.

When I tried to author a simple protobuf+grpc project, I had a ton of errors in the package provided config files. So, with 1c3335e I've updated them to the latest versions (3.2.0 and 1.1.2 respectively).

With those updated, I'm able to build and compile a simple hello-world application (based on the grpc examples).

cmake_minimum_required(VERSION 3.7)
project(test CXX)

# Compiler configurations
set(CMAKE_CXX_FLAGS_DEBUG "/MTd /Zi /Ob0 /Od /RTC1")
set(CMAKE_CXX_FLAGS_RELEASE "/MT /O2 /Ob2 /DNDEBUG")
add_definitions(-D_WIN32_WINNT=0x600)

# Protobuf
set(protobuf_MODULE_COMPATIBLE ON CACHE BOOL "")
find_package(Protobuf CONFIG REQUIRED)
message(STATUS "Using protobuf ${protobuf_VERSION}")

# gRPC
find_package(gRPC CONFIG REQUIRED)
message(STATUS "Using gRPC ${gRPC_VERSION}")

# gRPC C++ plugin
get_target_property(gRPC_CPP_PLUGIN_EXECUTABLE gRPC::grpc_cpp_plugin
    IMPORTED_LOCATION_RELEASE)

# Proto file
get_filename_component(hw_proto "helloworld.proto" ABSOLUTE)
get_filename_component(hw_proto_path "${hw_proto}" PATH)

# Generated sources
PROTOBUF_GENERATE_CPP(hw_proto_srcs hw_proto_hdrs "${hw_proto}")
...
mkdir build
cd build
cmake .. "-DCMAKE_TOOLCHAIN_FILE=D:\src\vcpkg\scripts\buildsystems\vcpkg.cmake" -DVCPKG_TARGET_TRIPLET=x86-windows-static -G "Visual Studio 14 2015"
cmake --build .
cmake --build . --config Release

I've included all the files so you can try it on your own enlistment to make sure it works for you: gprc-protobuf-test.zip.

Assuming that works, could you give me some more information about the compiler flags you're using in your project and the exact linker errors you're getting?

Yes, I was also specifying -DVCPKG_TARGET_TRIPLET:STRING=x64-windows-static (among other things).

Using this trick:

if(NOT VCPKG_TOOLCHAIN)
  # Configure FindBoost module to locate static libraries
  set(Boost_USE_STATIC_LIBS ON)
  set(Boost_USE_STATIC_RUNTIME ON)
endif()

doesn't require fiddling with boost libraries, and enables error-free build in debug mode. Thank you!

But building in release mode still fails with 170 linking errors of the style:

grpc++.lib(insecure_credentials.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '2' doesn't match value '0' in myCode.obj
grpc++.lib(insecure_credentials.obj) : error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MTd_StaticDebug' doesn't match value 'MT_StaticRelease' in myCode.obj
libcpmtd.lib(stdthrow.obj) : error LNK2038: mismatch detected for '_ITERATOR_DEBUG_LEVEL': value '2' doesn't match value '0' in myCode.obj
LINK : warning LNK4098: defaultlib 'LIBCMTD' conflicts with use of other libs; use /NODEFAULTLIB:library
grpc++.lib(channel_cc.obj) : error LNK2001: unresolved external symbol _CrtDbgReportW
grpc++.lib(channel_arguments.obj) : error LNK2019: unresolved external symbol _free_dbg referenced in function "private: void __cdecl std::_Yarn<char>::_Tidy(void)" (?_Tidy@?$_Yarn@D@std@@AEAAXXZ)
grpc++.lib(channel_arguments.obj) : error LNK2019: unresolved external symbol _malloc_dbg referenced in function "public: static void * __cdecl std::_Crt_new_delete::operator new(unsigned __int64,struct std::nothrow_t const &)" (??2_Crt_new_delete@std@@SAPEAX_KAEBUnothrow_t@1@@Z)

The reason for this is that debug/release mix-up was now always erring on the side of debug (as debug locations are listed before release locations in CMAKE_PREFIX_PATH variable in toolchain .cmake). The affected libraries: gRPC, OpenSSL, GoogleTest. Libraries which find properly both debug and release version: protobuf, tbb, zlib, boost.

I tried your self-contained example. Configuring with CMake produces these errors:

The CXX compiler identification is MSVC 19.0.24215.1

More messages, all OK. Then the error:

CMake Error at C:/Libs/vcpkg/installed/x64-windows-static/share/protobuf/protobuf-module.cmake:155 (endif):
  endif An ENDIF command was found outside of a proper IF ENDIF structure.
  Or its arguments did not match the opening IF command.
Call Stack (most recent call first):
  C:/Libs/vcpkg/installed/x64-windows-static/share/protobuf/protobuf-module.cmake:179 (_protobuf_find_libraries)
  C:/Libs/vcpkg/installed/x64-windows-static/share/protobuf/protobuf-config.cmake:9 (include)
  CMakeLists.txt:11 (find_package)

then 2 more error messages with the same content, and then

Found Protobuf: C:/Libs/vcpkg/installed/x64-windows-static/tools/protoc.exe (found version "3.0.2") 
Using protobuf 
Found ZLIB: optimized;C:/Libs/vcpkg/installed/x64-windows-static/lib/zlib.lib;debug;C:/Libs/vcpkg/installed/x64-windows-static/debug/lib/zlibd.lib (found version "1.2.11") 

Another error:

CMake Error at C:/Libs/vcpkg/installed/x64-windows-static/share/protobuf/protobuf-module.cmake:155 (endif):
  endif An ENDIF command was found outside of a proper IF ENDIF structure.
  Or its arguments did not match the opening IF command.
Call Stack (most recent call first):
  C:/Libs/vcpkg/installed/x64-windows-static/share/protobuf/protobuf-module.cmake:179 (_protobuf_find_libraries)
  C:/Libs/vcpkg/installed/x64-windows-static/share/protobuf/protobuf-config.cmake:9 (include)
  C:/Libs/vcpkg/installed/x64-windows-static/share/grpc/gRPCConfig.cmake:6 (find_package)
  CMakeLists.txt:15 (find_package)

this error repeated twice more. Finally:

Could NOT find Protobuf (missing:  Protobuf_LIBRARIES) (found version "3.0.2")
Found OpenSSL: C:/Libs/vcpkg/installed/x64-windows-static/debug/lib/ssleay32.lib (found version "1.0.2j") 
CMake Error at C:/Libs/vcpkg/installed/x64-windows-static/share/grpc/gRPCTargets.cmake:130 (message):
  The imported target "gRPC::gpr" references the file

     "C:/Libs/vcpkg/installed/lib/gpr.lib"

  but this file does not exist.  Possible reasons include:

  * The file was deleted, renamed, or moved to another location.

  * An install or uninstall procedure did not complete successfully.

  * The installation package was faulty and contained

     "C:/Libs/vcpkg/installed/x64-windows-static/share/grpc/gRPCTargets.cmake"

  but not all the files it references.

Call Stack (most recent call first):
  C:/Libs/vcpkg/installed/x64-windows-static/share/grpc/gRPCConfig.cmake:13 (include)
  CMakeLists.txt:15 (find_package)


Configuring incomplete, errors occurred!
See also "C:/Users/Dzenan/Desktop/gprc-protobuf-test/build/CMakeFiles/CMakeOutput.log".

I suppose these are the errors you were referring to and which 1c3335e fixes. I will update and try with it later.

With the updated vcpkg (and packages), your example both configures and builds fine, in both release and debug mode, despite the obvious debug/release library mix-up with OpenSSL:
screenshot 2017-02-17 10 21 21
Most likely explanation is that zlib and OpenSSL are not used directly by the project, so it does not matter.

Without any changes to the code, our project still fails to build in release mode (but does in debug mode). If I try changimg our CMake code like this:

if(VCPKG_TOOLCHAIN)
  add_definitions(-D_WIN32_WINNT=0x600)
  set(protobuf_MODULE_COMPATIBLE ON CACHE BOOL "")
  find_package(Protobuf CONFIG 3.0 REQUIRED)
  find_package(GRPC REQUIRED NO_MODULE)
  get_target_property(GRPC_CPP_PLUGIN gRPC::grpc_cpp_plugin IMPORTED_LOCATION_RELEASE)
  include(protobuf_generate_grpc_cpp)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DGTEST_LINKED_AS_SHARED_LIBRARY=1")
else()
  find_package(Protobuf 3.0 REQUIRED)
  find_package(GRPC REQUIRED)
  # Configure FindBoost module to locate static libraries
  set(Boost_USE_STATIC_LIBS ON)
  set(Boost_USE_STATIC_RUNTIME ON)
endif()
find_package(Boost 1.53.0 COMPONENTS filesystem program_options REQUIRED)

I get a CMake warning:

CMake Warning (dev) at C:/Dev/vcpkg/installed/x64-windows-static/share/protobuf/protobuf-module.cmake:130 (message):
  Variable Protobuf_SRC_ROOT_FOLDER defined, but not used in CONFIG mode
Call Stack (most recent call first):
  C:/Dev/vcpkg/installed/x64-windows-static/share/protobuf/protobuf-config.cmake:14 (include)
  CMakeLists.txt:59 (find_package)
This warning is for project developers.  Use -Wno-dev to suppress it.

and link errors in both debug

myCode.obj : error LNK2019: unresolved external symbol "class std::shared_ptr<class grpc::ChannelCredentials> __cdecl grpc::InsecureChannelCredentials(void)" (?InsecureChannelCredentials@grpc@@YA?AV?$shared_ptr@VChannelCredentials@grpc@@@std@@XZ) referenced in function ...
myCode.obj : error LNK2019: unresolved external symbol "class std::shared_ptr<class grpc::Channel> __cdecl grpc::CreateChannel(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::shared_ptr<class grpc::ChannelCredentials> const &)" (?CreateChannel@grpc@@YA?AV?$shared_ptr@VChannel@grpc@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@AEBV?$shared_ptr@VChannelCredentials@grpc@@@3@@Z) referenced in function ...
myCode.lib(myCode.obj) : error LNK2019: unresolved external symbol "public: __cdecl grpc::ClientContext::ClientContext(void)" (??0ClientContext@grpc@@QEAA@XZ) referenced in function ...
myCode.lib(myCode.obj) : error LNK2019: unresolved external symbol "public: __cdecl grpc::ClientContext::~ClientContext(void)" (??1ClientContext@grpc@@QEAA@XZ) referenced in function ...
myCode.lib(myCode.grpc.pb.obj) : error LNK2001: unresolved external symbol "class grpc::CoreCodegenInterface * grpc::g_core_codegen_interface" (?g_core_codegen_interface@grpc@@3PEAVCoreCodegenInterface@1@EA)
myCode.lib(myCode.grpc.pb.obj) : error LNK2001: unresolved external symbol "class grpc::GrpcLibraryInterface * grpc::g_glip" (?g_glip@grpc@@3PEAVGrpcLibraryInterface@1@EA)
etc

and release mode:

myCode.obj : error LNK2019: unresolved external symbol "class std::shared_ptr<class grpc::ChannelCredentials> __cdecl grpc::InsecureChannelCredentials(void)" (?InsecureChannelCredentials@grpc@@YA?AV?$shared_ptr@VChannelCredentials@grpc@@@std@@XZ) referenced in function ...
myCode.obj : error LNK2019: unresolved external symbol "class std::shared_ptr<class grpc::Channel> __cdecl grpc::CreateChannel(class std::basic_string<char,struct std::char_traits<char>,class std::allocator<char> > const &,class std::shared_ptr<class grpc::ChannelCredentials> const &)" (?CreateChannel@grpc@@YA?AV?$shared_ptr@VChannel@grpc@@@std@@AEBV?$basic_string@DU?$char_traits@D@std@@V?$allocator@D@2@@3@AEBV?$shared_ptr@VChannelCredentials@grpc@@@3@@Z) referenced in function ...
myCode.lib(myCode.obj) : error LNK2019: unresolved external symbol "public: __cdecl grpc::ClientContext::ClientContext(void)" (??0ClientContext@grpc@@QEAA@XZ) referenced in function ...
myCode.lib(myCode.obj) : error LNK2019: unresolved external symbol "public: __cdecl grpc::ClientContext::~ClientContext(void)" (??1ClientContext@grpc@@QEAA@XZ) referenced in function ...
myCode.lib(myCode.grpc.pb.obj) : error LNK2019: unresolved external symbol "class grpc::CoreCodegenInterface * grpc::g_core_codegen_interface" (?g_core_codegen_interface@grpc@@3PEAVCoreCodegenInterface@1@EA) referenced in function ...
myCode.lib(myCode.grpc.pb.obj) : error LNK2019: unresolved external symbol "class grpc::GrpcLibraryInterface * grpc::g_glip" (?g_glip@grpc@@3PEAVGrpcLibraryInterface@1@EA) referenced in function ...
etc.

The errors are only referring to grpc library.

Ah dear, yes, unfortunately find_package(OpenSSL) is not able to find the correct library. This is (kindof) a fundamental problem with CMake's multi-configuration-generator strategy + the builtin FindXYZ modules + libraries that don't have different DLL names in Release and Debug. Looking at the built-in FindOpenSSL.cmake module, however, it looks like it _would_ find the correct libs if we could get OpenSSL to generate libeay32d.lib and so forth. I'll see what we can do about that.

As a workaround, (though this gives an admittedly worse experience in Visual Studio) you can generate two separate build trees with -DCMAKE_BUILD_TYPE=Release and -DCMAKE_BUILD_TYPE=Debug, which should cause the correct libraries to be chosen for that configuration.

As for the grpc issue -- I believe that the errors you're getting indicate that you aren't linking in _enough_ of the runtime libraries. In the example (which appears to use some of the APIs you are having trouble linking), the following was sufficient:

  target_link_libraries(${_target}
    protobuf::libprotobuf
    gRPC::grpc++_unsecure)

Could you try adding a link_libraries(protobuf::libprotobuf gRPC::grpc++_unsecure) inside the if() block?

Incredibly, adding link_libraries(protobuf::libprotobuf gRPC::grpc++_unsecure) fixes the link errors. And the program runs in both debug and release mode.

Edit:
This works:

if(VCPKG_TOOLCHAIN)
  target_link_libraries(myCode
    INTERFACE
      protobuf::libprotobuf
      gRPC::grpc++_unsecure
      gRPC::grpc++
    )
else()
  target_link_libraries(myCode
    INTERFACE
      ${Protobuf_LIBRARIES}
      ${GRPC_ALL_LIBRARIES}
    )
endif()

this too:

if(VCPKG_TOOLCHAIN)
  find_package(GRPC REQUIRED NO_MODULE)
  set(GRPC_ALL_LIBRARIES gRPC::grpc++_unsecure gRPC::grpc++)
else()

These also explain why link_libraries(protobuf::libprotobuf gRPC::grpc++_unsecure) works.

Great to hear!

It's a design point of Vcpkg that you shouldn't _need_ specific code to handle us (though it can be the path of least resistance) -- if you'd like to drive out all the if(VCPKG_TOOLCHAIN)s, I'd be happy to help.

Thanks for the offer. I have concentrated all the differences in one place:

# Windows-specific configuration
if(MSVC)
  # Force building static libraries
  set(BUILD_SHARED_LIBS OFF)

  # Disable deprecation warnings for standard C and STL functions
  add_definitions(
    -D_CRT_SECURE_NO_DEPRECATE
    -D_CRT_NONSTDC_NO_DEPRECATE
    -D_CRT_SECURE_NO_WARNINGS
    -D_SCL_SECURE_NO_DEPRECATE
    -D_SCL_SECURE_NO_WARNINGS
    )
endif()

if(VCPKG_TOOLCHAIN)
  add_definitions(-D_WIN32_WINNT=0x600)
  set(protobuf_MODULE_COMPATIBLE ON CACHE BOOL "")
  find_package(Protobuf 3.0 REQUIRED NO_MODULE)
  find_package(GRPC REQUIRED NO_MODULE)
  get_target_property(GRPC_CPP_PLUGIN gRPC::grpc_cpp_plugin IMPORTED_LOCATION_RELEASE)
  include(protobuf_generate_grpc_cpp)
  set(CMAKE_CXX_FLAGS "${CMAKE_CXX_FLAGS} /DGTEST_LINKED_AS_SHARED_LIBRARY=1")
  set(GRPC_ALL_LIBRARIES gRPC::grpc++_unsecure gRPC::grpc++)
else()
  find_package(Protobuf 3.0 REQUIRED)
  find_package(GRPC REQUIRED)
  # Configure FindBoost module to locate static libraries
  set(Boost_USE_STATIC_LIBS ON)
  set(Boost_USE_STATIC_RUNTIME ON)
endif()

But before simplifying this we have to test how find_package with CONFIG (aka NO_MODULE) is working on Linux. Although building gRPC on Windows was a nightmare (before vcpkg version was fixed), building it on Linux did not go smoothly either (some conflicts with older version of libprotobuf pre-installed on Ubuntu).

I'll close this issue for now -- let me know if you'd like to continue here or via email at [email protected].

Was this page helpful?
0 / 5 - 0 ratings

Related issues

ThinkalVB picture ThinkalVB  路  3Comments

husseinalihazime picture husseinalihazime  路  3Comments

cjvaijo picture cjvaijo  路  3Comments

ghost picture ghost  路  3Comments

oahzuw picture oahzuw  路  3Comments