【问题标题】:Various libs in subfolders, how do I export them to the parent?子文件夹中的各种库,如何将它们导出到父文件夹?
【发布时间】:2016-04-26 17:44:02
【问题描述】:

我有一个这样的代码文件夹结构:

project/
  CMakeLists.txt
  src/
    project.cpp
  lib/
    libA/
      CMakeLists.txt
      src/
        libA.cpp
      include/
        libA.h
    libB/
      CMakeLists.txt
      src/
        libB.cpp
      include/
        libB.h

现在,libA 是完全独立的,所以我有一个类似于此的CMakeLists.txt

cmake_minimum_required(VERSION 2.8)
project ( libA )
set ( LIBA_INCLUDE_DIRECTORIES ${CMAKE_CURRENT_SOURCE_DIR}/include PARENT_SCOPE )
include_directories ( include/ )

file (GLOB LIBA_SOURCES ${CMAKE_CURRENT_SOURCE_DIR}
  src/libA.cpp
  )
add_library         ( libA STATIC ${LIBA_SOURCES} )

但是,libB 取决于 libA。如何导出libA.a 输出路径以用于链接到libB,以及相应的包含路径?

此外,我如何将该数据传递给主项目的父 CMakeLists.txt

【问题讨论】:

  • 有关可能性的讨论,请参见例如Making cmake library accessible by other cmake packages automatically。另一种可能性是通过 CMake 命令行参数(如 done here)将第一个库的路径传播到另一个库。
  • 如果我错了,@Florian 纠正我,但您的答案看起来像是针对有两个 单独 项目的情况。如果库作为子目录包含并且尚未构建,find_package 是否有效?
  • 你是对的。我以为您在问如何集成启用 CMake 的独立库。因此,在您的情况下,答案取决于您项目的设置/环境。 libA 是否也用于其他项目? libBlibA 是否具有相同的发布周期?您是否提供源文件和 CMake 文件并且目标构建环境未知?参见例如我的回答here 或我的问题here

标签: c++ cmake


【解决方案1】:

我没有亲自做过,所以这个答案可能只是一个起点,但 Qt 使用 cmake 并且能够将所有包含目录和库依赖项绑定到每个“模块”(库)中。如果你做到了,那么你可以简单地做

add_executable(myExe ...)
target_link_libraries(myExe libB)

到您的主 cmake 项目中的可执行文件。

查看 Qt 脚本,您需要设置的(对于每个库)似乎是:

add_library(libA ...)
set_target_properties(libA PROPERTIES
    "INTERFACE_LINK_LIBRARIES" "${LIBRARIES_LIBA_DEPENDS_ON}"
    "INTERFACE_INCLUDE_DIRECTORIES" "${LIBA_INCLUDE_DIRECTORIES}"
)

那么对于libB

add_library(libB ...)
set_target_properties(libB PROPERTIES
    "INTERFACE_LINK_LIBRARIES" "libA"
    "INTERFACE_INCLUDE_DIRECTORIES" "${LIBB_INCLUDE_DIRECTORIES}"
)

这种方法很酷的地方在于,如果操作得当,您可以将任意数量的库链接在一起,而不必担心依赖项和包含目录的组合爆炸。

【讨论】:

  • 我不知道这存在。这是包含目录的一个很好的解决方案。尽管您似乎可以不理会INTERFACE_LINK_LIBRARIES,因为默认情况下,cmake 的target_link_libraries() 依赖项本质上是可传递的。根据文档,设置INTERFACE_LINK_LIBRARIES 允许您覆盖通过target_link_libraries() 派生的链接依赖项。很有趣。
  • 如果有意义的话,您也可能依赖于尚未通过 add_library 创建的东西,尽管即使在这种情况下,我也可能会添加一个 IMPORTED 目标。
  • 另外,我认为如果您遇到 libC 依赖于 libB 依赖于 libA 的情况,但您,可执行设计者,只知道 libC(即 A 和 B 是实现细节),最好只需要target_link_libraries(myExe LibC)。每当我尝试在另一个库上 target_link_libraries 时,它似乎从来没有按预期工作。
  • target_link_libraries(myExe LibC) 在这种情况下绝对可以工作,而无需设置INTERFACE_LINK_LIBRARIES。也就是说,假设 LibC 将 CMake 目标命名为您的整个项目的一部分或导入的目标。
【解决方案2】:

以@epicbrew 和@NicolasHolthaus 的答案/cmets 为例,我只是想展示完整示例在新版本的 CMake 中的外观并讨论其中的一些含义:

project/CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project( project )

add_subdirectory( lib/libB )
add_subdirectory( lib/libA )

add_executable( project src/project.cpp )
target_link_libraries( project libB )

project/lib/libA/CMakeLists.txt

cmake_minimum_required(VERSION 3.0)
project( libA )

add_library( libA STATIC src/libA.cpp include/libA.h )
target_include_directories( libA PUBLIC include )

project/lib/libB/CMakeLists.txt

add_library( libB STATIC src/libB.cpp  include/libB.h )
target_include_directories( libB PUBLIC include )
target_link_libraries( libB libA )

如你所见

  1. 您需要在主文件CMakeLists.txt 中知道您还需要libA。我故意颠倒add_subdirectory() 调用以表明使用target_link_libraries() 时顺序无关紧要(允许目标前向声明)。
  2. 您不再需要本地的include_directories() 调用,它是target_include_directories() 的隐含部分。
  3. 对于 target_include_directories(... PUBLIC ...),这些包含路径是自传播的。
  4. 您不需要添加 ${CMAKE_CURRENT_SOURCE_DIR} 前缀,这些是默认值。
  5. 我刚刚将源/头文件直接添加到 add_executable()/add_library() 调用中,以使此示例代码更加紧凑。通常我也会将它们放在局部变量中。
  6. libB 不是独立的,但您可以更改它,例如通过将add_subdirectory( ../libA libA ) 添加到其CMakeLists.txt 文件中。显然,您必须从主 CMakeLists.txt 中删除 add_subdirectory( lib/libA )
  7. 如果您想稍微优化 CMake 处理,则不应重复 project() 命令。如果您仍然希望 libA 自给自足,可以在 cmake_minimum_required(VERSION 3.0)/project( libA ) 调用周围添加 if ( CMAKE_SOURCE_DIR STREQUAL CMAKE_CURRENT_SOURCE_DIR )/endif()

【讨论】:

    【解决方案3】:

    CMake 了解您已构建的所有目标,因此无需将信息导出到父级。您只需告诉 CMake libB 与 libA 的链接,CMake 负责将正确的路径放在链接器命令行上。因此,只需在 libB/CMakeLists.txt 中将 libA 指定为 libB 的链接依赖项:

    target_link_libraries(libB libA)
    

    对于包含路径,您需要按照顶级项目/CMakeLists.txt 中的以下行在父级中设置变量:

    set(LIBA_INCLUDES libA/include)
    set(LIBB_INCLUDES libB/include)
    

    然后在子项目 CMakeList.txt 文件中引用这些。

    或者,您可以在 libB/CMakeLists.txt 文件中使用相对路径:

    include_directories(../libA/include)
    

    【讨论】:

    • 难道你不能通过在库上设置一个目标属性来获取包含目录,类似于 Qt5 所做的吗?
    • @NicolasHolthaus 可能,尽管我不熟悉那种特殊的方法。我很想看看它是如何工作的,也许你可以发布一个答案?
    猜你喜欢
    • 2017-06-07
    • 1970-01-01
    • 2019-05-01
    • 1970-01-01
    • 2015-05-30
    • 2021-08-02
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多