How to use libraries within my CMake project that need to be installed first?

前端 未结 3 438
执笔经年
执笔经年 2020-12-16 03:19

I have a problem with my CMake build system. There are CMakeLists.txt files defining runtimes or libraries or using ExternalProjects_Add() to downl

3条回答
  •  感情败类
    2020-12-16 04:06

    Thanks @Tsyvarev and @tamas.kenez you for the two good answers. I ended up using the super-build pattern. The top-level project doesn't do much at configure time. At build time, it runs external CMake processes to configure, build and install the projects.

    Usually, this is implemented using ExternalProject_Add() instead of add_subdirectory() to add the projects. I found add_custom_command() to work better since it doesn't do additional tasks in the background like creating stamp files and so on.

    # add_project( [DEPENDS project...])
    function(add_project PROJECT)
        cmake_parse_arguments(PARAM "" "" "DEPENDS" ${ARGN})
        add_custom_target(${PROJECT} ALL DEPENDS ${PARAM_DEPENDS})
        # Paths for this project
        set(SOURCE_DIR  ${CMAKE_CURRENT_SOURCE_DIR}/${PROJECT})
        set(BUILD_DIR   ${CMAKE_CURRENT_BINARY_DIR}/${PROJECT})
        set(INSTALL_DIR ${CMAKE_INSTALL_PREFIX}/${PROJECT})
        # Configure
        escape_list(CMAKE_MODULE_PATH)
        escape_list(CMAKE_PREFIX_PATH)
        add_custom_command(TARGET ${TARGET}
            COMMAND ${CMAKE_COMMAND}
                --no-warn-unused-cli
                "-DCMAKE_MODULE_PATH=${CMAKE_MODULE_PATH_ESCAPED}"
                "-DCMAKE_PREFIX_PATH=${CMAKE_PREFIX_PATH_ESCAPED}"
                -DCMAKE_BINARY_DIR=${BUILD_DIR}
                -DCMAKE_INSTALL_PREFIX=${INSTALL_DIR}
                -DCMAKE_CXX_FLAGS=${CMAKE_CXX_FLAGS}
                ${SOURCE_DIR}
            WORKING_DIRECTORY ${BUILD_DIR})
        # Build
        add_custom_command(TARGET ${TARGET}
            COMMAND ${CMAKE_COMMAND}
                --build .
                --target install
            WORKING_DIRECTORY ${BUILD_DIR})
        # Help later find_package() calls
        append_global(CMAKE_PREFIX_PATH ${INSTALL_DIR})
    endfunction()
    

    Here are the two helper functions. It took me quite some time to figure out the right way to pass list parameters to other CMake processes without them being interpreted and passes as multiple parameters.

    # escape_list()
    function(escape_list LIST_NAME)
        string(REPLACE ";" "\;" ${LIST_NAME}_ESCAPED "${${LIST_NAME}}")
        set(${LIST_NAME}_ESCAPED "${${LIST_NAME}_ESCAPED}" PARENT_SCOPE)
    endfunction()
    
    # append_global( value...)
    function(append_global NAME)
        set(COMBINED "${${NAME}}" "${ARGN}")
        list(REMOVE_DUPLICATES COMBINED)
        set(${NAME} "${COMBINED}" CACHE INTERNAL "" FORCE)
    endfunction()
    

    The only downside is that every project needs to have an install target for this. So you need to add a dummy install command like install(CODE "") to projects that have no install command otherwise, e.g. those who just call ExternalProject_Add.

提交回复
热议问题