CMake 的 FetchContent 与 Makefile 构建的依赖项

Posted

技术标签:

【中文标题】CMake 的 FetchContent 与 Makefile 构建的依赖项【英文标题】:CMake's FetchContent with Makefile-built dependency 【发布时间】:2019-10-31 13:33:05 【问题描述】:

我当前的项目需要一个使用 Makefile 构建的库。我想在我的项目编译期间编译这个库;此功能是 FetchContent 的主要卖点,它与 CMake 依赖项配合得非常好。不过,我无法让它与 Makefile 一起工作,也找不到有关如何做到这一点的示例。

FetchContent_Declare(
    make_lib
    URL http://url/library_code.tar.gz 
    BUILD_COMMAND $CMAKE_COMMAND -E env make -j 8
    BUILD_IN_SOURCE true  
    BINARY_DIR ""
)

FetchContent_GetProperties(make_lib)
if (NOT make_lib_POPULATED)
    FetchContent_Populate(make_lib)

    # here I would like to declare imported libraries:
    add_library(make_lib::libA STATIC IMPORTED GLOBAL)
    target_include_directories(make_lib::libA INTERFACE $make_lib_SOURCE_DIR/include)
    set_property(TARGET make_lib::libA   PROPERTY IMPORTED_LOCATION <path to "to be built" lib>)

endif()
make 的“编译时”执行是否可能? 如果是这样,它可以并行吗? 是否可以使用依赖编译库声明导入的目标?

【问题讨论】:

"make 的“编译时”执行是否完全可能"...这是什么意思?无论如何,运行make通常被认为是编译时,所以我不确定为什么它不会在你的情况下。您能否更具体地说明何时您希望make 为该库运行? 使用 CMake 时,脚本通常可以在两个不同的阶段执行,配置时间(运行 make 命令时)或编译时间(执行生成器时...对于 case ninja,或 make) .上面的例子有一个嵌套的makefile,可以在任何时候执行,但是我很难让它在后一种情况下工作。如果我没有正确地提出问题,我很抱歉。但是你的回答并没有完全解决我的疑惑。 好吧,这更有意义。我最初并没有完全理解你的问题。我更新了我的答案,详细介绍了一种可能的解决方案。希望这会有所帮助! 【参考方案1】:

FetchContent_* 命令只是获取来自特定外部资源的内容或元数据,并填充 CMake 变量;它们实际上并不执行任何配置、构建或安装步骤。因此,在调用FetchContent_Declare() 时,与这些步骤相关的任何选项都会显式忽略。这包括以下选项:

CONFIGURE_COMMAND BUILD_COMMAND INSTALL_COMMAND TEST_COMMAND

来自FetchContent 文档:

此模块允许在配置时通过ExternalProject 模块支持的任何方法填充内容。 ExternalProject_Add() 在构建时下载,FetchContent 模块使内容立即可用,允许配置步骤在命令中使用内容,如 add_subdirectory()include()file() 操作。

这不适用于您的用例,因为像 add_subdirectory() 这样的调用会失败,因为外部库中没有 CMake 文件。

正如this 帖子中所述,ExternalProject_Add() 在您的情况下更有意义。您的调用可能如下所示:

ExternalProject_Add(make_lib
    DOWNLOAD_DIR $CMAKE_CURRENT_BINARY_DIR
    URL http://url/library_code.tar.gz
    UPDATE_COMMAND ""
    SOURCE_DIR $make_lib_SOURCE_DIR
    BUILD_IN_SOURCE 1
    CONFIGURE_COMMAND ""
    BUILD_COMMAND "make -j8"
    INSTALL_COMMAND "$make_lib_install_commands"
)

add_library(make_lib_libA STATIC IMPORTED GLOBAL)
set_property(TARGET make_lib_libA 
    PROPERTY IMPORTED_LOCATION 
    $make_lib_SOURCE_DIR/path/to/make_lib_libA.a
)

add_dependencies(myOtherLib make_lib)

注意,这里的BUILD_COMMAND 不会被忽略,而是会在编译时运行make -j8。您还应该能够将导入的库声明为您的代码布局。但重要的是,请记住调用add_dependencies(),它告诉CMake 你的make_lib 被另一个目标使用;否则,make-lib不会构建。

调用ExternalProject_Add()后,可以使用ExternalProject_Get_Property()查询外部项目目标的信息。链接示例显示了如何获取项目的源目录,这对于获取构建库的位置很有用。

【讨论】:

您能否更简洁地说明它的外观?我的示例编写了一个被忽略的 BUILD_COMMAND,并且该库没有配置、测试或安装。您仍然没有解释如何声明您刚刚下载的内容的导入目标。而且我完全不明白 cmake 如何将作业数量转发到嵌套的 makefile,对于初学者来说,我使用 ninja 来构建。

以上是关于CMake 的 FetchContent 与 Makefile 构建的依赖项的主要内容,如果未能解决你的问题,请参考以下文章

通过 CMake FetchContent 设置 Qt5

为啥 FetchContent 更喜欢子目录包含而不是安装依赖项?

CMake 快速入门

CMake 快速入门

CMake 快速入门

Mac 下 CMake 的配置与使用