chrisdembia chrisdembia - 1 month ago 12
C++ Question

How does CMake determine the fallback build configuration for target dependencies?

Background



On Windows, I use CMake (3.2.3) with Visual Studio 2015 (Community) to build a C++ project named Foo. Foo consists of a single shared library, named foo. I build and install Foo in both the Debug and Release configurations. I set
CMAKE_DEBUG_POSTFIX
to
_d
so that the installation of Foo contains both
foo.dll
and
foo_d.dll
, as well as CMake config files
FooConfig.cmake
,
FooTargets.cmake
,
FooTargets-debug.cmake
, and
FooTargets-release.cmake
.

Now, I start building a separate CMake project Bar, which consists of an executable 'bar', and which depends on the library foo. But I build bar in the RelWithDebInfo configuration. When I try to run bar from Visual Studio, it looks for
foo_d.dll
instead of
foo.dll
.

That is, the RelWithDebInfo configuration falls back onto the Debug configuration if a dependency does not exist under the RelWithDebInfo configuration.

Here is the CMakeLists.txt for Foo:

project(Foo)
cmake_minimum_required(VERSION 3.2.3)
add_library(foo SHARED foo.h foo.cpp)
install(TARGETS foo EXPORT FooTargets DESTINATION
RUNTIME DESTINATION bin LIBRARY DESTINATION lib
ARCHIVE DESTINATION lib)
configure_package_config_file(FooConfig.cmake.in
"${CMAKE_BINARY_DIR}/FooConfigToInstall.cmake"
INSTALL_DESTINATION cmake)
install(FILES ${CMAKE_BINARY_DIR}/FooConfigToInstall.cmake"
DESTINATION cmake RENAME FooConfig.cmake)
install(EXPORT FooTargets DESTINATION cmake)


Here is the CMakeLists.txt for Bar:

project(Bar)
cmake_minimum_required(VERSION 3.2.3)
find_package(Foo)
add_executable(bar bar.cpp)
target_link_libraries(bar foo)


Question



How does CMake determine which configuration to fall back on for dependencies, if the desired configuration is unavailable for a dependency?

Followup: Is it possible to control this fallback? Ideally, there would be no fallback. I want CMake to give an error like "Cannot build bar in RelWithDebInfo configuration, because dependency foo is not available in that configuration (available configurations: Debug, Release).

Answer

Script FooConfig.cmake which is generated with Foo package provides IMPORTED library target foo, which is used by Bar for link with.

CMake has a notion about "Map from project configuration to IMPORTED target’s configuration.", which is determined by properties MAP_IMPORTED_CONFIG_<CONFIG>:

Set this to the list of configurations of an imported target that may be used for the current project’s configuration. Targets imported from another project may not provide the same set of configuration names available in the current project. Setting this property tells CMake what imported configurations are suitable for use when building the <CONFIG> configuration. The first configuration in the list found to be provided by the imported target is selected. If this property is set and no matching configurations are available, then the imported target is considered to be not found.

You may add to the end of script FooConfig.cmake.in something like

set_target_property(foo PROPERTIES MAP_IMPORTED_CONFIG_RELWITHDEBINFO "prohibited")

so it will be impossible to link with foo in RelWithDebInfo configuration (because foo target doesn't provide configuration named "prohibited").