【问题标题】:Exporting an imported library导出导入的库
【发布时间】:2015-09-08 08:08:14
【问题描述】:

我有 projectA,我正在向其中导入一个库:

add_library(foo STATIC IMPORTED)
set_property(TARGET foo PROPERTY IMPORTED_LOCATION /path/to/foo.a)

然后我在项目中的几个地方使用foo,一切正常。

在几个目录级别下,我想导出在此项目中构建的库,以便在另一个具有完全断开连接的 CMake 配置的项目中使用。我有:

...
target_link_libraries(thislib foo)
export(TARGETS thislib FILE /path/to/thislib.cmake)

导入的 projectB 也需要 foo(因为导入的库需要它),并抱怨它 cannot find -lfoo。我尝试将其添加到 export 命令中,但随后我得到:

CMake Error at thislib/CMakeLists.txt:37 (export):
  export given target "foo" which is not built by this project.

我只想将我在本地使用的相同配置导出到另一个(导入)项目。我不想明确告诉 projectB foo。有没有办法做到这一点?

【问题讨论】:

    标签: cmake


    【解决方案1】:

    我没有找到解决问题的实际解决方案,但我发布了我自己的解决方法以供将来参考。

    我意识到foo 依赖正在 在导出中发出;它只是没有路径。由于我还没有弄清楚如何让 cmake 将路径与它一起导出,我将export 命令恢复为上面问题中显示的命令(没有foo)。

    然后我回到原来导入foo的地方,删除了add_libraryset_property,替换为:

    set(foo /path/to/foo.a)
    

    然后将target_link_libraries改为:

    target_link_libraries(thislib ${foo})
    

    换句话说,它不是一个真正的“导入库”,而是一个原始库路径。这确实会正确写入导出文件并允许 projectB 链接。

    【讨论】:

    • 其中一个问题是它不允许您设置目标属性,例如 INTERFACE_INCLUDE_DIRECTORIES
    • 另一个问题是您的路径将被复制到导出脚本中,这使得它们很可能在其他机器上无用。
    【解决方案2】:

    我也找不到理想的方法来做到这一点。但这是我现在使用的解决方法。这是额外的工作,而不是 DRY,但我认为它实现了正确的目标。

    想象一下 lib B 依赖于第三方 lib AA 要么定义了一个查找模块,要么我们可以为它实现一个自定义查找模块。两者都是可行的。假设我们已经编写了FindA.cmake 并存储在${CMAKE_SOURCE_DIR}/cmake 中。另外,假设当你运行cmake生成B的构建系统时,你提供了A_ROOT来帮助cmake定位A

    那么在B的顶层CMakeLists.txt我们需要:

    # Use FindA.cmake, defined in the cmake/ directory.
    list(APPEND CMAKE_MODULE_PATH "${CMAKE_SOURCE_DIR}/cmake")
    find_package(A REQUIRED)
    
    # ... (define the B target here)...
    
    # Copy the config file to the build dir
    configure_file(cmake/BConfig.cmake cmake/BConfig.cmake @ONLY)
    
    # Copy A's find module (skip this step if FindA is not custom).
    configure_file(cmake/FindA.cmake cmake/FindA.cmake COPYONLY)
    
    # Create the target export.
    export(EXPORT BTargets
        FILE ${CMAKE_BINARY_DIR}/cmake/BTargets.cmake
        NAMESPACE B::
        )
    
    # Register B so cmake can find B's config file.
    export(PACKAGE B)
    

    现在在cmake/BConfig.cmake:

    # Get the exported find module onto the module path.
    # This step is unnecessary if the find module is part of cmake.
    list(APPEND CMAKE_MODULE_PATH "${CMAKE_CURRENT_LIST_DIR}")
    
    # Store the root so that find_package will work.
    # You may have to store other variables here, too, but
    # the principle is just more of the same.
    set(A_ROOT @A_ROOT@) # **Could optionally cache this.
    find_package(A MODULE REQUIRED)
    
    # The usual one-liner for a config file.
    include("${CMAKE_CURRENT_LIST_DIR}/BTargets.cmake")
    

    为了解决问题,让我们看第二个示例,Rocket 这次使用Boost,它已经定义了一个查找模块。

    CMakeLists.txt:

    option(Boost_USE_MULTITHREADED ON)
    option(Boost_USE_STATIC_LIBS OFF)
    find_package(Boost REQUIRED COMPONENTS filesystem program_options)
    
    add_library(Rocket rocket.cpp)
    target_link_libraries(Rocket
        PUBLIC Boost::filesystem
        PRIVATE Boost::program_options
        )
    
    configure_file(cmake/BConfig.cmake cmake/BConfig.cmake @ONLY)
    
    export(EXPORT RocketTargets 
        FILE ${CMAKE_BINARY_DIR}/RocketTargets.cmake
        NAMESPACE Rocket::
        )
    export(PACKAGE Rocket)
    

    那么cmake/RocketConfig.cmake 会:

    set(BOOST_ROOT @BOOST_ROOT@ CACHE PATH "In case boost was relocated.")
    set(Boost_USE_MULTITHREADED @Boost_USE_MULTITHREADED@)
    set(Boost_USE_STATIC LIBS @Boost_USE_STATIC_LIBS@)
    find_package(Boost REQUIRED COMPONENTS filesystem program_options)
    
    include("${CMAKE_CURRENT_LIST_DIR}/RocketTargets.cmake")
    

    是的。好像打字太多了。似乎 cmake 应该能够从 CMakeLists.txt 中的 export 语句生成所有这些代码。但这个策略似乎达到了目的。

    另外,我还没有测试过,但我怀疑,如果你的传递依赖使用CONFIG 而不是MODULE 来表示find_package,那么添加到CMAKE_PREFIX_PATH 应该很容易你的配置文件也是如此。

    编辑(2021):Tsyvarev 指出了find_dependency() 宏,它可以在一定程度上简化上述方法。考虑使用它而不是 find_package()

    【讨论】:

    • 是的,在脚本BConfig.cmake 中添加对A 包的搜索是正确的方法。但最好通过find_dependency 宏来实现该搜索,而不是find_package。该宏自动从调用find_package(B) 中“派生” REQUIRED 选项,如果找不到A,它也会打印B 包的名称。在使用 find_dependency 宏之前,应该包含定义它的 CMake 脚本:include(CMakeFindDependencyMacro)
    • 感谢您指出这一点。当时我不知道那个宏,但是文档说得很清楚。这就是 CMake 的问题 - 它在细节中有很好的记录,但它非常复杂,很难找出存在哪些功能以及如何最好地使用它们。
    【解决方案3】:

    你需要

    1. 从构建位置导出foo(例如,foolibs.cmake
    2. 而不是直接使用/path/to/thislib.cmakeexport(TARGETS thislib...生成的导出文件创建另一个,thislib-and-deps.cmake,其中包括两者:

      include(<...>/foolibs.cmake)
      include(${CMAKE_CURRENT_LIST_DIR}/thislib.cmake)
      

    【讨论】:

    • 是否存在一个简单的解决方案来以可重用的方式实现这一点,而无需为每个项目实现大量 CMake 代码?这似乎不是 CMake-way-to-go。目前我对每个传递依赖重复find_package(&lt;name&gt; CONFIG) 调用,这也很糟糕,因为它打破了传递依赖的概念。
    猜你喜欢
    • 2014-09-14
    • 2020-03-27
    • 2017-05-17
    • 2014-06-10
    • 2011-01-15
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    相关资源
    最近更新 更多