./CMakeLists.txt
cmake_minimum_required(VERSION 3.0.2 FATAL_ERROR)
file(GLOB_RECURSE MAIN_SOURCE RELATIVE ${CMAKE_CURRENT_SOURCE_DIR} *.h *.cpp)
add_executable(main ${MAIN_SOURCE})
target_include_directories(main PRIVATE ${CMAKE_CURRENT_SOURCE_DIR})
find_package(CURL REQUIRED)
target_link_libraries(main PRIVATE ${CURL_LIBRARIES})
target_include_directories(main PRIVATE ${CURL_INCLUDE_DIRS})
find_package(ZLIB REQUIRED)
target_link_libraries(main PRIVATE ZLIB::ZLIB)
find_library(CPR_LIBRARY cpr)
target_link_libraries(main PRIVATE ${CPR_LIBRARY})
target_include_directories(main PRIVATE ${CPR_INCLUDE_DIRS})
if(MSVC)
target_link_libraries(main PRIVATE Ws2_32 Wldap32 Crypt32)
target_compile_options(main PRIVATE "$<$<CONFIG:Debug>:/MTd>")
target_compile_options(main PRIVATE "$<$<CONFIG:Release>:/MT>")
target_compile_options(main PRIVATE "$<$<CONFIG:MinSizeRel>:/MT>")
target_compile_options(main PRIVATE "$<$<CONFIG:RelWithDebInfo>:/MT>")
else()
find_package(OpenSSL REQUIRED)
target_link_libraries(main PRIVATE OpenSSL::SSL OpenSSL::Crypto)
endif()
vcpkg/triplets/x64-windows-static.cmake
set(VCPKG_TARGET_ARCHITECTURE x64)
set(VCPKG_CRT_LINKAGE static)
set(VCPKG_LIBRARY_LINKAGE static)
set(CURL_USE_WINSSL ON)
./build.bat
pushd build
vcpkg install --triplet x64-windows-static curl[ssl] cpr
cmake .. -G "Visual Studio 15 2017 Win64" -T "host=x64" -DVCPKG_TARGET_TRIPLET="x64-windows-static" -DCMAKE_TOOLCHAIN_FILE="vcpkg\scripts\buildsystems\vcpkg.cmake"
cmake --build . --config Release
popd
Link:
C:\Program Files (x86)\Microsoft Visual Studio\2017\Community\VC\Tools\MSVC\14.15.26726\bin\HostX64\x64\link.exe /ERRORREPORT:QUEUE /OUT:"C:\projects
\c\main\build\src\Release\main.exe" /INCREMENTAL:NO /NOLOGO /LIBPATH:"C:\projects\c\main\vendor\vcpkg\installed\x64
-windows\lib" /LIBPATH:"C:\projects\c\main\vendor\vcpkg\installed\x64-windows\lib\manual-link" "..\..\..\vendor\vcpkg\installed\x64-windows-static\debug\lib\libcurl.lib" "..\..\..\vendor\vcpkg\install
ed\x64-windows-static\lib\zlib.lib" "..\..\..\vendor\vcpkg\installed\x64-windows-static\debug\lib\cpr.lib" Ws2_32.lib Wldap32.lib Crypt32.lib kernel32.lib user32.lib gdi32.lib winspool.lib shell32.lib ole32.lib oleaut32.
lib uuid.lib comdlg32.lib advapi32.lib "C:\projects\c\main\vendor\vcpkg\installed\x64-windows\lib\*.lib" /MANIFEST /MANIFESTUAC:"level='asInvok
er' uiAccess='false'" /manifest:embed /PDB:"C:/projects/c/main/build/src/Release/main.pdb" /SUBSYSTEM:CONSOLE /TLBID:1 /D
YNAMICBASE /NXCOMPAT /IMPLIB:"C:/projects/c/main/build/src/Release/main.lib" /MACHINE:X64 /machine:x64
cpr.lib(session.cpp.obj) : error LNK2038: mismatch detected for 'RuntimeLibrary': value 'MTd_StaticDebug' doesn't match value 'MT_StaticRelease' in main.obj [C:\projects\c\main\build\src\main.vcxproj]
...
As we can see under Release configuration, the linker was trying to link curl and cpr at debug\lib\libcurl.lib and debug\lib\cpr.lib, which are both for Debug configuration that caused the errors. However zlib was resolved correctly to its Release version.
However if I try to compile the Debug configuration, zlib will be correctly resolved to debug\lib\zlibd.lib again, and other libraries unchanged, so everything compiles.
How to make it select the correct libcurl and cpr libraries for Release (this also happens for MinSizeRel and RelWithDebInfo) configurations?
After debugging with buildsystems\vcpkg.cmake, I realized that vcpkg will add ${_VCPKG_INSTALLED_DIR}/${VCPKG_TARGET_TRIPLET}/debug to the search path if CMAKE_BUILD_TYPE is set to Debug or empty, and this debug path is always in higher precedence than the release path.
That means if I want to find a library from the release path, I have to set -DCMAKE_BUILD_TYPE="Release" or anything not Debug or empty to remove the Debug path from the search paths. I've tested it and the errors went away.
However according to CMake's document on CMAKE_BUILD_TYPE:
This variable is only meaningful to single-configuration generators (such as Makefile Generators and Ninja) i.e.
MSVC on the other hand is a multi-configuration generator, so we shouldn't be required to set this variable at all. Moreover, once this variable is set, and the include path is fixed for one generated build system, for example:
cd build\x64-windows-static
cmake ..\.. -G "Visual Studio 15 2017 Win64" -T "host=x64" -DVCPKG_TARGET_TRIPLET="x64-windows-static" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_TOOLCHAIN_FILE="vcpkg\scripts\buildsystems\vcpkg.cmake"
cmake --build . --config Release
It's impossible to use this generated build system to build the Debug configuration:
cd build\x64-windows-static
cmake --build . --config Debug
REM THIS WILL FAIL!
This fails because the libraries will be found at the Release path, but the project is configured for Debug.
Therefore, if I want to build my project both in Release and Debug, I have to generate two separate build systems:
cd build\x64-windows-static-dbg
cmake ..\.. -G "Visual Studio 15 2017 Win64" -T "host=x64" -DVCPKG_TARGET_TRIPLET="x64-windows-static" -DCMAKE_BUILD_TYPE="Debug" -DCMAKE_TOOLCHAIN_FILE="vcpkg\scripts\buildsystems\vcpkg.cmake"
cmake --build . --config Debug
cd build\x64-windows-static-rel
cmake ..\.. -G "Visual Studio 15 2017 Win64" -T "host=x64" -DVCPKG_TARGET_TRIPLET="x64-windows-static" -DCMAKE_BUILD_TYPE="Release" -DCMAKE_TOOLCHAIN_FILE="vcpkg\scripts\buildsystems\vcpkg.cmake"
cmake --build . --config Release
I think this is a design mistake of vcpkg. Because CMake provided features like cmake-generator-expressions to support building multiple configs from one generated build system, as long as the generator supports this, like MSVC.
So I propose that vcpkg should add support to set the correct library search paths in build stage using generator expressions, according to the current building configuration.
zlib resolved correctly because it has different file names for its Release (zlib.lib) and Debug (zlibd.lib) versions. Therefore when I leave the CMAKE_BUILD_TYPE as empty, both the Release and Debug paths will be included, and thus the correct version of the library would always be found.
See the discussion in #1626. Especially the last comment of @ras0219-msft. I wonder whether this has ever been implemented?!
As vcpkg has already override find_package and other find_xxx functions, what about exporting more variables for each configuration for use with generator expressions?
# Inside find_package(CURL)
# find inside debug prefix, export to CURL_LIBRARIES_DEBUG
# find again inside release prefix, export to CURL_LIBRARIES_RELEASE
# Usage
find_package(CURL)
target_link_libraries(main PRIVATE $<IF:$<CONFIG:Debug>,
${CURL_LIBRARIES_DEBUG},
${CURL_LIBRARIES_RELEASE} >)
So that you don't need to generate multiple solutions just for different configurations. Also only the Debug config needs to be treated differently, other default configs (Release, MinSizeRel, and RelWithDebInfo) are all compatible with each other since they all link to Release CRT.
Is there a workaround for this, without resorting to creating two projects with setting CMAKE_BUILD_TYPE?
Here's my workaround:
#
# dependency_link - Looks for a package, or library, with a CONFIG or
# non config mode, and links to the required target and adds include dirs to
# the target as needed. This will ensure the proper debug/release libs are
# discoverd and it works around a few vcpkg quirks to do this.
#
# usage:
# dependency_link(engsup Boost COMPONENTS log stack)
# dependency_link(engsup libcurl)
#
macro(dependency_link target name)
set(options CONFIG)
set(multiArgs COMPONENTS TARGETS)
set(oneValueArgs ALIAS)
set(components ${ARGN})
cmake_parse_arguments(dependency_link "${options}" "${oneValueArgs}" "${multiArgs}" ${ARGN})
if (dependency_link_CONFIG)
msg("Looking Config based dependency ${name}")
else()
msg("Looking dependency ${name}")
endif()
if (dependency_link_COMPONENTS)
msg(" Components: ${dependency_link_COMPONENTS}")
endif()
if (dependency_link_ALIAS)
msg(" Alias: ${dependency_link_ALIAS}")
endif()
# Try to find it
if (dependency_link_COMPONENTS)
msg("Locating dependency ${name} with components ${dependency_link_COMPONENTS}")
find_package(${name} QUIET ${dependency_link_OPTIONS} COMPONENTS ${dependency_link_COMPONENTS})
if (${name}_FOUND)
message("Found package: ${name}")
else()
message(FATAL_ERROR "Did not find package: ${name} With components: ${dependency_link_COMPONENTS}")
endif()
elseif(dependency_link_CONFIG)
msg("Locating config based dependency ${name}")
find_package(${name} CONFIG REQUIRED)
if (NOT ${name}_FOUND)
message(FATAL_ERROR "Failed to locate config based dependency ${name}")
endif()
else()
msg("Looking for non config based dependency ${name}")
msg("Debug root search path: ${DEPS_ROOT_LIB_DEBUG}")
msg("Release root search path: ${DEPS_ROOT_LIB_RELEASE}")
set(DEBUG_NAMES ${name} ${name}d lib${name} lib${name}d)
set(RELEASE_NAMES ${name} ${name}d lib${name})
if (dependency_link_ALIAS)
set(DEBUG_NAMES ${DEBUG_NAMES} ${dependency_link_ALIAS} ${dependency_link_ALIAS}d lib${dependency_link_ALIAS} lib${dependency_link_ALIAS}d)
set(RELEASE_NAMES ${RELEASE_NAMES} ${dependency_link_ALIAS} lib${dependency_link_ALIAS})
endif()
msg("Looking for: ${DEBUG_NAMES} ${RELEASE_NAMES}")
find_library(${name}_debug_libs NAMES ${DEBUG_NAMES} PATHS ${DEPS_ROOT_LIB_DEBUG} NO_DEFAULT_PATH)
find_library(${name}_release_libs NAMES ${RELEASE_NAMES} PATHS ${DEPS_ROOT_LIB_RELEASE} NO_DEFAULT_PATH)
set(debug_libs ${${name}_debug_libs})
set(release_libs ${${name}_release_libs})
if (NOT debug_libs OR NOT release_libs)
message(FATAL_ERROR "Failed to locate both debug and release library paths for target: ${name} Debug lib: ${debug_libs} Release lib: ${release_libs}")
endif()
msg("Linking target: ${target} to dependency: ${name}" "debug: ${debug_libs}" "release: ${release_libs}")
target_link_libraries(${target} PUBLIC debug ${debug_libs} optimized ${release_libs})
endif()
# Log the target libs debug and release libs
foreach(dep ${dependency_link_TARGETS})
if (NOT TARGET ${dep})
message(FATAL_ERROR "Failed to locate target: ${dep} for dependency: ${name}")
endif()
get_target_property(LIBS_DEBUG ${dep} IMPORTED_LOCATION_DEBUG)
get_target_property(LIBS_RELEASE ${dep} IMPORTED_LOCATION_RELEASE)
if (NOT LIBS_DEBUG OR NOT LIBS_RELEASE)
message(FATAL_ERROR "One or more targets have incorrect imports for dependency: ${dep}me Debug lib: ${LIBS_DEBUG} Release lib: ${LIBS_RELEASE}")
endif()
msg("Linking target: ${target} to dependency: ${name}" "debug: ${LIBS_DEBUG}" "release: ${LIBS_RELEASE}")
target_link_libraries(${target} PUBLIC ${dep})
endforeach()
# If includes were defined, add them
if (${name}_INCLUDE_DIRS)
msg("Including package directory dependency: ${name} To target: ${target} Include dirs: ${${name}_INCLUDE_DIRS}")
target_include_directories(${target} PUBLIC ${${name}_INCLUDE_DIRS})
endif()
# If the dependency is boost we have to include another definition
if (${name} STREQUAL "Boost" AND ${name}_LIBRARIES)
msg("Linking target: ${target} To boost lib list:" ${${name}_LIBRARIES})
target_link_libraries(${target} PUBLIC ${${name}_LIBRARIES})
endif()
endmacro()
Usage example:
add_library(myTarget ...)
dependency_link(myTarget azurestorage ALIAS wastorage)
dependency_link(myTarget cpprestsdk CONFIG TARGETS cpprestsdk::cpprest)
dependency_link(myTarget aws-cpp-sdk-core CONFIG TARGETS aws-cpp-sdk-core)
dependency_link(myTarget aws-cpp-sdk-s3 CONFIG TARGETS aws-cpp-sdk-s3)
dependency_link(myTarget Boost COMPONENTS log system)
dependency_link(myTarget curl)
dependency_link(myTarget ssl)
dependency_link(myTarget crypto)
dependency_link(myTarget xml2)
dependency_link(myTarget archive)
dependency_link(myTarget zlib ALIAS z)
dependency_link(myTarget bcrypt CONFIG TARGETS bcrypt::bcrypt)
dependency_link(myTarget lzma)
dependency_link(myTarget bz2)
dependency_link(myTarget lzo2)
This issue hasn鈥檛 been updated in a year, if it is still an issue, please reopen.
Most helpful comment
As vcpkg has already override
find_packageand otherfind_xxxfunctions, what about exporting more variables for each configuration for use with generator expressions?So that you don't need to generate multiple solutions just for different configurations. Also only the Debug config needs to be treated differently, other default configs (Release, MinSizeRel, and RelWithDebInfo) are all compatible with each other since they all link to Release CRT.