Manuzor
Manuzor

Reputation: 1826

Generator Expression for install() commands

The docs for CMake 3.2.2 state, that it is possible to use generator expressions for the install(<FILES|PROGRAMS> ...) signature. I was trying to use generator expressions in other signatures of install(), but apparently it is not working. I would like to do something like this:

install(TARGETS foo EXPORT fooConfig
        RUNTIME DESTINATION "Bin/$<CONFIG>"
        LIBRARY DESTINATION "Lib/$<CONFIG>"
        ARCHIVE DESTINATION "Lib/$<CONFIG>"

I also tried calling install() multiple times like this:

install(TARGETS foo EXPORT fooConfig
        RUNTIME DESTINATION "Bin/Debug" CONFIGURATIONS Debug
        LIBRARY DESTINATION "Lib/Debug" CONFIGURATIONS Debug
        ARCHIVE DESTINATION "Lib/Debug" CONFIGURATIONS Debug
install(TARGETS foo EXPORT fooConfig
        RUNTIME DESTINATION "Bin/Release" CONFIGURATIONS Release
        LIBRARY DESTINATION "Lib/Release" CONFIGURATIONS Release
        ARCHIVE DESTINATION "Lib/Release" CONFIGURATIONS Release
install(TARGETS foo EXPORT fooConfig
        RUNTIME DESTINATION "Bin/MinSizeRel" CONFIGURATIONS MinSizeRel
        LIBRARY DESTINATION "Lib/MinSizeRel" CONFIGURATIONS MinSizeRel
        ARCHIVE DESTINATION "Lib/MinSizeRel" CONFIGURATIONS MinSizeRel
install(TARGETS foo EXPORT fooConfig
        RUNTIME DESTINATION "Bin/RelWithDebInfo" CONFIGURATIONS RelWithDebInfo
        LIBRARY DESTINATION "Lib/RelWithDebInfo" CONFIGURATIONS RelWithDebInfo
        ARCHIVE DESTINATION "Lib/RelWithDebInfo" CONFIGURATIONS RelWithDebInfo

This causes CMake to emit an error along the lines of Target 'foo' exported more than once in 'fooConfig'.

I cannot use CMAKE_BUILD_TYPE here either without updating the CMake cache and re-running the build. Rather than that, I want to use the Visual Studio batch-build feature, which builds multiple configurations for me.

There was a little hack that I tried too. I noticed that CMake's generated INSTALL project is simply invoking a CMake script ${CMAKE_BINARY_DIR}/cmake_install.cmake with the argument -DBUILD_TYPE=$(Configuration). So I tried the following:

install(TARGETS foo EXPORT fooConfig
        RUNTIME DESTINATION "Bin/\${BUILD_TYPE}"
        LIBRARY DESTINATION "Lib/\${BUILD_TYPE}"
        ARCHIVE DESTINATION "Lib/\${BUILD_TYPE}"

This actually worked well for the installation. However, the installed export script, i.e. the output of install(EXPORT fooConfig DESTINATION .) now also tries to use ${BUILD_TYPE}, which is not set at the time the user includes that script...

If anyone knows another way to accomplish my goals, please let me know.

Upvotes: 10

Views: 6590

Answers (2)

sakra
sakra

Reputation: 65931

Unluckily the install command only supports generator expressions for the list of files to be installed, but not for the destination directory.

I would recommend on sticking with your little hack, but use CMAKE_INSTALL_CONFIG_NAME instead of CMAKE_BUILD_TYPE, i.e.:

install(TARGETS foo EXPORT fooConfig
        RUNTIME DESTINATION "Bin/\${CMAKE_INSTALL_CONFIG_NAME}"
        LIBRARY DESTINATION "Lib/\${CMAKE_INSTALL_CONFIG_NAME}"
        ARCHIVE DESTINATION "Lib/\${CMAKE_INSTALL_CONFIG_NAME}"

CMAKE_INSTALL_CONFIG_NAME is set to the actual build configuration used for installing in the generated cmake_install.cmake script.

The generated export scripts (e.g., fooConfig-debug.cmake) can be automatically fixed by adding a patch script to the installation process. Generate a file patch_export_files.cmake with the following contents in the source directory:

file(GLOB_RECURSE _configFiles "$ENV{DESTDIR}${CMAKE_INSTALL_PREFIX}/fooConfig-*.cmake")
foreach(_configFile ${_configFiles})
    file (READ "${_configFile}" _contents)
    string (REGEX MATCH "configuration \"[A-Za-z]+\"" _configName "${_contents}")
    if (_configName MATCHES "\"([A-Za-z]+)\"")
        message(STATUS "Patching: ${_configFile}")
        string (REPLACE "\${CMAKE_INSTALL_CONFIG_NAME}" "${CMAKE_MATCH_1}" _patchedContents "${_contents}")
        file (WRITE "${_configFile}" "${_patchedContents}")
    endif()
endforeach()

The patch script need to be run at installation time by adding an install(SCRIPT ... call after the install(EXPORT ...:

install(EXPORT fooConfig DESTINATION .)
...
install(SCRIPT patch_export_files.cmake)

The patch script first parses the configuration that the generated export script is valid for from its header comment and then replaces each use of ${CMAKE_INSTALL_CONFIG_NAME} with the configuration name.

Upvotes: 10

leodotcloud
leodotcloud

Reputation: 1960

I am working on a project which requires builds with different configurations. The way I have achieved this is using "ExternalProject".

I have created a sample repository to show you the idea: https://github.com/mpaluru/cmake_multiple_build_configs_example

(Linux is the environment I use mostly and don't have access to Visual Studio.) If you pass on the -G flag in the top level CMakeLists.txt, you should be able to generate your VS files. I have tested this on Linux and "make -j" works fine. Both the Debug and Release configurations build in parallel.

Summary: You create a new superbuild project which calls your project using ExternalProject_Add with different CMAKE_BUILD_TYPE.

And based on the build type, you pass different definitions, or do install differently.

Upvotes: 2

Related Questions