【问题标题】:Programmatically get all targets in a CMake project以编程方式获取 CMake 项目中的所有目标
【发布时间】:2020-02-15 02:53:58
【问题描述】:

我想让一个特定目标依赖于我项目中所有其他添加的目标。换一种说法——我希望这个目标(比如lint)在所有库和应用程序构建完成后运行。

在指定project 的顶部CMakeLists.txt 文件中是否有办法获取使用add_subdirectory 添加的目录中其他CMakeLists.txt 文件添加的所有目标的列表?然后我可以使用add_dependencies 来指定顺序。有一个BUILDSYSTEM_TARGETS 属性,但它只适用于目录级别。

如果有其他方法可以实现这一点,请告诉我。我使用 CMake 3.14。

【问题讨论】:

  • 您检查过that question 的第二个答案和that question 的两个答案吗?
  • 感谢您的积分。是的,我做到了。提到了BUILDSYSTEM_TARGETS,但它只适用于当前目录,而不适用于整个树。

标签: cmake


【解决方案1】:

为了将来的参考,我最终编写了自己的函数而不是宏。但这个概念与@thomas_f 接受的答案相同。

代码如下:

# Collect all currently added targets in all subdirectories
#
# Parameters:
# - _result the list containing all found targets
# - _dir root directory to start looking from
function(get_all_targets _result _dir)
    get_property(_subdirs DIRECTORY "${_dir}" PROPERTY SUBDIRECTORIES)
    foreach(_subdir IN LISTS _subdirs)
        get_all_targets(${_result} "${_subdir}")
    endforeach()

    get_directory_property(_sub_targets DIRECTORY "${_dir}" BUILDSYSTEM_TARGETS)
    set(${_result} ${${_result}} ${_sub_targets} PARENT_SCOPE)
endfunction()

【讨论】:

    【解决方案2】:

    你没有提到你的 CMake 版本,所以我假设 3.8 或更好,这个解决方案已经过测试。

    一种可能的解决方案是遍历项目中的所有子目录,然后将BUILDSYSTEM_TARGETS 应用于每个子目录。为了简单和可读性,我把它分成三个不同的宏。

    首先,我们需要一种递归获取项目中所有子目录的方法。为此,我们可以使用file(GLOB_RECURSE ...) 并将LIST_DIRECTORIES 设置为ON

    #
    # Get all directories below the specified root directory.
    #   _result     : The variable in which to store the resulting directory list
    #   _root       : The root directory, from which to start.
    #
    macro(get_directories _result _root)
        file(GLOB_RECURSE dirs RELATIVE ${_root} LIST_DIRECTORIES ON ${_root}/*)
        foreach(dir ${dirs})
            if(IS_DIRECTORY ${dir})
                list(APPEND ${_result} ${dir})
            endif()
        endforeach()
    endmacro()
    

    其次,我们需要一种方法来获取特定目录级别的所有目标。 DIRECTORY 带有一个可选参数,即您要查询的目录,这是它工作的关键:

    #
    # Get all targets defined at the specified directory (level).
    #   _result     : The variable in which to store the resulting list of targets.
    #   _dir        : The directory to query for targets.
    #
    macro(get_targets_by_directory _result _dir)
        get_property(_target DIRECTORY ${_dir} PROPERTY BUILDSYSTEM_TARGETS)
        set(_result ${_target})
    endmacro()
    

    第三,我们需要另一个宏将所有这些联系在一起:

    #
    # Get all targets defined below the specified root directory.
    #   _result     : The variable in which to store the resulting list of targets.
    #   _root_dir   : The root project root directory
    #
    macro(get_all_targets _result _root_dir)
        get_directories(_all_directories ${_root_dir})
        foreach(_dir ${_all_directories})
            get_targets_by_directory(_target ${_dir})
            if(_target)
                list(APPEND ${_result} ${_target})
            endif()
        endforeach()
    endmacro()
    

    最后,这里是你如何使用它:

    get_all_targets(ALL_TARGETS ${CMAKE_CURRENT_LIST_DIR})
    

    ALL_TARGETS 现在应该是一个列表,其中包含在调用者目录级别下创建的每个目标的名称。请注意,它包括在当前CMakeLists.txt 中创建的任何目标。为此,您可以额外致电get_targets_by_directory(ALL_TARGETS ${CMAKE_CURRENT_LIST_DIR})

    【讨论】:

    • 太棒了。为什么你必须检查if(IS_DIRECTORY ${dir})file() 不是只返回目录吗?
    • @ilya1725 不,它也列出了文件。
    • @ilya1725 LIST_DIRECTORIES ON 只是指示file() 也包含目录,使用GLOB_RECURSE 时默认关闭。
    • 当我尝试这段代码时,IS_DIRECTORY 没有将(相对)目录路径验证为目录。因此,宏 get_directories 并没有真正起作用。也许这是因为我运行的是 CMake v3.18.2?
    • @Smartskaft2 刚刚使用 CMake 3.19 进行了尝试,它似乎按预期工作。也许尝试 Ilya 的解决方案(另一个答案)?
    猜你喜欢
    • 1970-01-01
    • 2023-04-09
    • 2020-11-11
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 1970-01-01
    • 2015-05-28
    • 1970-01-01
    相关资源
    最近更新 更多