ilya1725
ilya1725

Reputation: 4958

Programmatically get all targets in a CMake project

I'd like to have one specific target be dependent on all the other added targets in my project. To say it in a different way - I'd like this target (say lint) to run after all the libraries and applications were built.

Is there a way in the top CMakeLists.txt file, where the project is specified, to get the list of all targets added by other CMakeLists.txt files in the directories added using add_subdirectory? Then I can use the add_dependencies to specify the order. There is a BUILDSYSTEM_TARGETS property, but it only works on a directory level.

If there is some other way to achieve this, please let me know. I use CMake 3.14.

Upvotes: 8

Views: 7999

Answers (2)

ilya1725
ilya1725

Reputation: 4958

For the future reference, I've ended up writing my own function instead of a macro. But the concept is the same as the accepted answer by @thomas_f.

Here is the code:

# 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()

Upvotes: 11

thomas_f
thomas_f

Reputation: 1993

You did not mention your CMake version, so I will assume 3.8 or better, for which this solution has been tested.

One possible solution is to iterate through all sub-directories in your project, and then apply BUILDSYSTEM_TARGETS to each of them. For the sake of simplicity and readability, I have split this up into three different macros.

First, we need a way of recursively obtaining all sub-directories in the project. For this, we can use file(GLOB_RECURSE ...) with LIST_DIRECTORIES set to 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()

Secondly, we need a way to obtain all targets at a particular directory level. DIRECTORY takes an optional parameter, namely the directory you wish to query, which is key for this to work:

#
# 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()

Thirdly, we need another macro to tie all this together:

#
# 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()

Finally, here is how you can use it:

get_all_targets(ALL_TARGETS ${CMAKE_CURRENT_LIST_DIR})

ALL_TARGETS should now be a list holding the names of every target created below the caller's directory level. Note that it does not include any targets created in the current CMakeLists.txt. For that, you can make an extra call to get_targets_by_directory(ALL_TARGETS ${CMAKE_CURRENT_LIST_DIR}).

Upvotes: 11

Related Questions