cmake 对外部项目使用find_package()

qnyhuwrf  于 2023-03-12  发布在  其他
关注(0)|答案(3)|浏览(222)

我有一个名为messages的外部项目。我正在使用ExternalProject_Add来获取和构建该项目。
如果我在顶层CMakeLists.txt中使用find_package(messages REQUIRED)cmake ..会失败,因为它找不到包安装文件,这是合乎逻辑的,因为它们只在make命令调用期间构建。
我不确定是否有方法在ExternalProjects上使用find_package()。如果有,请给我一个例子。
谢谢BhanuKiran

yhived7q

yhived7q1#

您误解了ExternalProject的工作原理。您 * 不能 * find_package(messages REQUIRED),因为它还没有构建。ExternalProject只是创建构建子项目所需的构建步骤。
您有两种选择:
1.使用add_subdirectoryFetchContent代替ExternalProject。在这种情况下,您不需要find_package调用。这有效地将子项目添加到主构建中并导入子项目的目标。
1.使用 * 两个 * ExternalProject调用:一个用于messages,另一个用于main_project,这取决于messages。如果messages使用export(EXPORT)函数,你可以将CMAKE_PREFIX_PATHmessages_ROOT指向build目录,否则你需要运行messages的安装步骤,并在build目录中设置一个安装前缀。然后main_project内部的find_project(messages REQUIRED)调用将成功,这可能需要重新构造您的构建。
一般来说,ExternalProject只在定义 * super-build * 时有用,super-build是一系列相互依赖的CMake构建,而super-build只在需要完全不同的配置时选项时有用,比如不同的工具链(例如,你正在交叉编译,但需要一个代码生成器在编译机器上运行)。如果不是这样,最好使用FetchContentadd_subdirectory加上git子模块。
最好在CMake 3.14+中使用FetchContent,因为它添加了FetchContent_MakeAvailable宏,可以减少样板文件。
文件:
https://cmake.org/cmake/help/latest/module/ExternalProject.htmlhttps://cmake.org/cmake/help/latest/module/FetchContent.html

zyfwsgd6

zyfwsgd62#

因为我喜欢保持我的CMake文件不可知我如何得到我的包。
我使用的是FetchContent,添加了这个文件(Findalib.cmake):

if(NOT alib_POPULATED)
    set(alib_BUILD_TESTS OFF CACHE BOOL INTERNAL)
    set(alib_BUILD_EXAMPLES OFF CACHE BOOL INTERNAL)
    set(alib_BUILD_DOCS OFF CACHE BOOL INTERNAL)
    FetchContent_MakeAvailable(alib)
endif()
set(alib_FOUND TRUE)

然后,在我的CMake文件中:

find_package(alib REQUIRED)
target_link_libraries(my-executable PUBLIC alib::alib)

这样,包只在我声明依赖关系的文件中声明,并且只有当我试图找到它们时才获取它们。

ddrv8njm

ddrv8njm3#

从@Alex Reinking的回答来看,通常情况下,最好少一些样板文件,并且只在超级构建时使用ExternalProject。然而,有时库不支持FetchContent(),就像@Blackclaws指出的那样。也就是说,使用ExternalProject_Add()更好一些,随着我们继续开发C++项目或我们的项目变得更大,这一点对我们来说会很清楚。
下面是一个没有FetchContent_Declare()find_package()的替代解决方案,尽管它没有经过彻底测试。在没有外部包管理器的纯CMake中可以做到这一点。请参阅下面的详细信息部分。事先了解目标是有帮助的(.lib/.dll/.so/.dylib),并包括由ExternalProject_Add()命令返回的 * 实用程序 * 导出的目录、命名空间等。
您可以修改此示例以包含official docs中的更多可用功能

TL;DR:详细信息

在配置项目时,我们可以使用一个虚拟库来使cmake触发构建,而不会导致ExternalProject_Add()出错,并传递创建目标的Debug配置的步骤,我们可以在项目中使用另一个目标的Release配置,等等。
1.您需要通过CMAKE_CACHE_ARGS而不是CMAKE_ARGS将构建和安装参数指定给ExternalProject_Add(),因为它们被临时参数叠加,并被结转(特别是在IDE中,如Visual Studio代码。如果您的项目使用调试构建,但ExternalProject指定发布构建)
1.将Location/to/the/install/directory/of/your/target附加到CMAKE_INSTALL_PREFIX
1.创建临时目录文件(MAKE_DIRECTORY Location/to/the/include/directory/of/your/target)。这将在配置时抑制 * 位置不存在 * 错误。
1.下一步是创建一个虚拟的 * 导入接口库 *,它是链接到主可执行文件的目标。这一步需要设置IMPORTED_LOCATIONINTERFACE_INCLUDE_DIRECTORIES以及其他属性。文档在这里,cmake-properties中。
1.最后,add_dependencies(dummy_target_name exported_target_name)删除输出中抛出的错误
(The返回的目标在带有CMakeTools扩展的Visual Studio代码中被称为实用程序。我在这里也将其称为实用程序。不确定它在原生cmake中是否被称为实用程序)

示例

我们将使用ExternalProject_Add()构建GLFW

初始项目结构

Project
    |
    |-src
    |   |-main.cpp
    |   
    |-CMakeLists.txt
    |
    |-cmake
    |   |-BuildExternalProject.cmake
    |   |-FindGLFW.cmake

主要CMakeLists.txt文件:

cmake_minimum_required(VERSION 3.25.2)
project("My_Project" VERSION 1.0.0 LANGUAGES C CXX)

set(CMAKE_MODULE_PATH "${PROJECT_SOURCE_DIR}/cmake")
include(BuildExternalProject)
include(FindGlfw)

add_executable(exec src/main.cpp)

target_link_libraries(exec
    PUBLIC
        glfw_target
)

FindGLFW.cmake

BuildExternalProject(
    NAME            glfw
    LIB_NAME        glfw_target
    GIT_REPO        https://github.com/glfw/glfw.git
    GIT_TAG         3.3.8
    INSTALL_PATH    ${PROJECT_SOURCE_DIR}/external
    BUILD_TYPE      Release
    ARCHITECTURE    x64
)

cmake/构建外部项目。cmake:

include(ExternalProject)

macro(append_cmake_prefix_path)
    list(APPEND CMAKE_PREFIX_PATH ${ARGN})
    string(REPLACE ";" "|" CMAKE_PREFIX_PATH "${CMAKE_PREFIX_PATH}")
endmacro()

function(BuildExternalProject)
    set(one_value_args
        NAME            # name of directory in <INSTALL_DIR>/NAME"
        LIB_NAME        # For example, the glfw library needs us to link against the target glfw3 not glfw
        GIT_REPO        # URL : https://github.com/user/repo.git
        GIT_TAG         # Release tags: "3.3.8","2.3.2" ...
        INSTALL_PATH        # "${PROJECT_SOURCE_DIR}/external" in this example
        BUILD_TYPE      # "Release", "Debug" ...
        ARCHITECTURE    # "x64","x86"...
    )
    set(multi_value_args
        EXTRA_CMAKE_ARGS
        EXT_BUILD_ARGS
        EXT_INSTALL_ARGS
    )

    # Parse arguments.
    cmake_parse_arguments(
        _ARGS # Main prefix for parsed args
        "${options}"
        "${one_value_args}"
        "${multi_value_args}"
        ${ARGN} # Number of Args
    )

    # Set new path
    set(INSTALL_PATH_NEW_PREFIX ${_ARGS_INSTALL_PATH}/${_ARGS_ARCHITECTURE}-${_ARGS_BUILD_TYPE}/${_ARGS_NAME})

    set(INSTALL_PATH_LIB ${INSTALL_PATH_NEW_PREFIX}/lib)
    set(INSTALL_PATH_INCLUDE ${INSTALL_PATH_NEW_PREFIX}/include)

    set(_UTILITY_NAME ${_ARGS_NAME}_utility)

    ExternalProject_Add(
        ${_UTILITY_NAME}
        PREFIX ${_UTILITY_NAME}
        GIT_REPOSITORY ${_ARGS_GIT_REPO}
        GIT_TAG ${_ARGS_GIT_TAG}

        CMAKE_CACHE_ARGS # CMAKE_ARGS does not use the FORCE option, this is a problem especially in IDEs
            "-DCMAKE_BUILD_TYPE:STRING=${_ARGS_BUILD_TYPE}"

        # This is where the install will
        "-DCMAKE_INSTALL_PREFIX:PATH=${INSTALL_PATH_NEW_PREFIX}"

        # Addition parsed args
        ${_ARGS_EXTRA_CMAKE_ARGS}

        # Commands to build
        BUILD_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target install --config ${_ARGS_BUILD_TYPE}
        COMMAND ${_ARGS_EXT_BUILD_ARGS}

        # Commands to install
        INSTALL_COMMAND ${CMAKE_COMMAND} --build <BINARY_DIR> --target install --config ${_ARGS_BUILD_TYPE}
        COMMAND ${_ARGS_EXT_INSTALL_ARGS}

        CONFIGURE_HANDLED_BY_BUILD ON # No config steps are passed
        BUILD_ALWAYS FALSE
        UPDATE_DISCONNECTED TRUE
    )

    # We append to `CMAKE_INSTALL_PREFIX`
    append_cmake_prefix_path(${INSTALL_PATH_NEW_PREFIX})

    # We make the temporary directory
    file(MAKE_DIRECTORY ${INSTALL_PATH_INCLUDE})

    # We make the new dummy imported library
    add_library(${_ARGS_LIB_NAME} INTERFACE IMPORTED GLOBAL ${_UTILITY_NAME})

    # Setup the include and libs/dlls,etc
    target_include_directories(${_ARGS_LIB_NAME} INTERFACE ${INSTALL_PATH_INCLUDE})
    target_link_libraries(${_ARGS_LIB_NAME} INTERFACE ${INSTALL_PATH_LIB}/*.lib)

    add_dependencies(${_ARGS_LIB_NAME} ${_UTILITY_NAME})
endfunction()

最终项目结构

Project
    |
    |-src
    |   |-main.cpp
    |   
    |-external
    |   |-x64-Debug
    |   |   |-glfw                                          # Everything under this is auto generated
    |   |       |-include/GLFW/..         
    |   |       |-lib                     
    |   |       |   |-cmake/*Config.cmake; *.cmake          # This is where we need to point CMAKE_INSTALL_PREFIX
    |   |       |   |                                       # so find_package() can find it you need it to in the future
    |   |       |   |-pkgconfig/*.pc                    
    |   |                            
    |   |-x64-Release                                          
    |       |-glfw                                          # Everything under this is auto generated
    |           |-include/GLFW/..         
    |           |-lib                     
    |           |   |-cmake/*Config.cmake; *.cmake          # This is where we need to point CMAKE_INSTALL_PREFIX
    |           |   |                                       # so find_package() can find it you need it to in the future
    |           |   |-pkgconfig/*.pc
    |                                
    |   
    |
    |-CMakeLists.txt
    |
    |-cmake
    |   |-BuildExternalProject.cmake
    |   |-FindGLFW.cmake

获取内容声明与外部项目添加

1.由于FetchContent_Declare()使目标在配置时可用,并且如果源代码非常大,您可能不希望在配置时下载或构建所有库,特别是如果您的项目只需要外部项目中的几个子目标。这些参数可以作为可选参数通过ExternalProject_Add()传递到子项目中。
1.从docs of FetchContent_Declare

  • contentOptions* 可以是ExternalProject_Add()命令理解的任何下载、更新或修补选项。配置、生成、安装和测试步骤被显式禁用,因此与它们相关的选项将被忽略
  1. ExternalProject_Add()提供了一个更大的选项集,可以在配置、构建、安装、测试等阶段运行。它本身就是一个项目,可以帮助直接公开完全或部分构建的二进制文件、库等。

来源

  1. https://cmake.org/cmake/help/latest/module/ExternalProject.html
  2. https://www.youtube.com/watch?v=nBptg3SHPGU&ab_channel=JeffersonAmstutz
  3. https://github.com/jeffamstutz/superbuild_ospray
  4. https://github.com/geospace-code/h5fortran/blob/main/cmake/hdf5.cmake
  5. https://github.com/geospace-code/h5fortran/blob/main/cmake/h5fortran.cmake
  6. https://github.com/deepmind/mujoco/blob/main/cmake/FindOrFetch.cmake

相关问题