# CMake List of all Project Targets

**URL:** https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077
**Category:** Code
**Created:** [April 22, 2020, 6:36pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077 "2020-04-22T18:36:28Z")
**Posts on this page:** 17
**Page:** 1

<div class="post-metadata">

### Author: ![developer](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/d/bcef8e/32.png) [@developer](https://discourse.cmake.org/u/developer)
#### Post date: [April 22, 2020, 6:36pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/1 "2020-04-22T18:36:28Z")

</div>

I have a top level project CMakeLists.txt file with multiple CMakeLists.txt below added with add\_subdirectory that define targets.

Is there a way to list all of the targets from my top level project?

---

<div class="post-metadata">

### Author: ![McMartin](https://discourse.cmake.org/user_avatar/discourse.cmake.org/mcmartin/32/150_2.png) [@McMartin](https://discourse.cmake.org/u/McMartin)
#### Post date: [April 22, 2020, 6:40pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/2 "2020-04-22T18:40:12Z")

</div>

There is the `BUILDSYSTEM_TARGETS` directory property which was introduced in CMake 3.7. See [https://cmake.org/cmake/help/latest/prop\_dir/BUILDSYSTEM\_TARGETS.html](https://cmake.org/cmake/help/latest/prop_dir/BUILDSYSTEM_TARGETS.html) for more details.

---

<div class="post-metadata">

### Author: ![developer](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/d/bcef8e/32.png) [@developer](https://discourse.cmake.org/u/developer)
#### Post date: [April 22, 2020, 7:15pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/3 "2020-04-22T19:15:50Z")

</div>

Thanks for the response!

Would I then have to iterate all directories/subdirectories and get that property on each one?

---

<div class="post-metadata">

### Author: ![McMartin](https://discourse.cmake.org/user_avatar/discourse.cmake.org/mcmartin/32/150_2.png) [@McMartin](https://discourse.cmake.org/u/McMartin)
#### Post date: [April 22, 2020, 7:28pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/4 "2020-04-22T19:28:49Z")

</div>

Exactly.

You can use the [`SUBDIRECTORIES` directory property](https://cmake.org/cmake/help/latest/prop_dir/SUBDIRECTORIES.html) to make this generic.

---

<div class="post-metadata">

### Author: ![developer](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/d/bcef8e/32.png) [@developer](https://discourse.cmake.org/u/developer)
#### Post date: [April 22, 2020, 9:23pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/5 "2020-04-22T21:23:49Z")

</div>

Does CMake not support recursive functions?

I have something like

```auto
function(getAllSubdirs dir dirs)
    message("called with dir (${dir}) and dirs (${dirs})")
    set(_dirs "")
    # get subdirectories for dir
    get_property(subdirs DIRECTORY ${dir} PROPERTY SUBDIRECTORIES)
    # iterate any found subdirectories
    foreach(subdir ${subdirs})
        # append each sub directory
        list(APPEND _dirs ${subdir})
        set(${dirs} _dirs PARENT_SCOPE)
        getAllSubdirs(${subdir} ${_dirs})
    endforeach()
endfunction()

set(dirs ".")
getAllSubDirs(. ${dirs})
message("all dirs " ${dirs})

```

It seems like any recursive calls to getAllSubdirs doesn’t actually pass the updated \_dirs to that function…

---

<div class="post-metadata">

### Author: ![McMartin](https://discourse.cmake.org/user_avatar/discourse.cmake.org/mcmartin/32/150_2.png) [@McMartin](https://discourse.cmake.org/u/McMartin)
#### Post date: [April 22, 2020, 9:28pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/6 "2020-04-22T21:28:14Z")

</div>

```cmake
set(dirs ".")
getAllSubDirs(. ${dirs})

```

is equivalent to

```cmake
getAllSubDirs(. ".")

```

I don’t think that is what you wanted to write.

---

<div class="post-metadata">

### Author: ![developer](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/d/bcef8e/32.png) [@developer](https://discourse.cmake.org/u/developer)
#### Post date: [April 22, 2020, 10:29pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/7 "2020-04-22T22:29:43Z")

</div>

That’s besides the point. Even when I tried to write a basic recursive function in CMake, it didn’t work as expected. It seems CMake doesn’t like recursively adding onto a list of some kind. For example, the following will execute forever…

```auto
function(simpleRecursive param)
    list(APPEND param "4;")
    set(${param} ${param} PARENT_SCOPE)
    if(NOT "${param}" STREQUAL "4;4;4;") # would end if STREQUAL "4;4;"
        simpleRecursive(${param})
    endif()
endfunction()

set(foo "4;")
simpleRecursive(${foo})
message(${foo})

```

So instead I made an iterative solution.

```auto
set(Project_Directories "")
get_property(stack DIRECTORY ${CMAKE_SOURCE_DIR} PROPERTY SUBDIRECTORIES)
while(stack)
    list(POP_BACK stack directory)
    list(APPEND Project_Directories ${directory})
    get_property(subdirs DIRECTORY ${directory} PROPERTY SUBDIRECTORIES)
    if(subdirs)
        list(APPEND stack ${subdirs})
    endif()
endwhile()

function(getTargets foundTargets)
    foreach(dir ${Project_Directories})
        get_property(target DIRECTORY ${dir} PROPERTY BUILDSYSTEM_TARGETS)
        list(APPEND targets ${target})
    endforeach()
    set(${foundTargets} ${targets} PARENT_SCOPE)
endfunction()

getTargets(foo)

message("all targets" ${foo})

```

---

<div class="post-metadata">

### Author: ![McMartin](https://discourse.cmake.org/user_avatar/discourse.cmake.org/mcmartin/32/150_2.png) [@McMartin](https://discourse.cmake.org/u/McMartin)
#### Post date: [April 22, 2020, 10:39pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/8 "2020-04-22T22:39:03Z")

</div>

Calling a function opens a new scope, so it is possible to make a recursive function, but you need to call `set(... PARENT_SCOPE)` after doing the recursive call.

---

<div class="post-metadata">

### Author: ![developer](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/d/bcef8e/32.png) [@developer](https://discourse.cmake.org/u/developer)
#### Post date: [April 22, 2020, 10:49pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/9 "2020-04-22T22:49:43Z")

</div>

I appreciate the continued feedback.

I still don’t understand your suggestion though. Can you fix the `simpleRecursive` function I made above to show what you mean? I can’t seem to get that simple example to work, despite what I set to `PARENT_SCOPE`.

---

<div class="post-metadata">

### Author: ![McMartin](https://discourse.cmake.org/user_avatar/discourse.cmake.org/mcmartin/32/150_2.png) [@McMartin](https://discourse.cmake.org/u/McMartin)
#### Post date: [April 22, 2020, 10:57pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/10 "2020-04-22T22:57:09Z")

</div>

I’m replying from my phone, so I didn’t check that it works, but I would write it like this:

```cmake
function(simpleRecursive in_out_var)
    list(APPEND ${in_out_var} "4;") # effectively appends to "foo" 
    if(NOT "${${in_out_var}}" STREQUAL "4;4;4;")
        simpleRecursive(${in_out_var})
    endif()
    set(${in_out_var} ${${in_out_var}} PARENT_SCOPE) # effectively sets "foo" in the parent scope
endfunction()

set(foo "4;")
simpleRecursive(foo) # pass the variable name, not its value 
message("${foo}")

```

---

<div class="post-metadata">

### Author: ![developer](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/d/bcef8e/32.png) [@developer](https://discourse.cmake.org/u/developer)
#### Post date: [April 22, 2020, 11:03pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/11 "2020-04-22T23:03:42Z")

</div>

You’re better than I if you can do that from your phone 🙂

Unfortunately when I ran that it resulted in the same issue where the recursive call will never complete.

Out of curiosity, what do the double brackets mean, `${${in_out_var}}`? If that wasn’t intentional, I tried your example without too, with no difference.

---

<div class="post-metadata">

### Author: ![McMartin](https://discourse.cmake.org/user_avatar/discourse.cmake.org/mcmartin/32/150_2.png) [@McMartin](https://discourse.cmake.org/u/McMartin)
#### Post date: [April 23, 2020, 7:23am UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/12 "2020-04-23T07:23:33Z")

</div>

The recursion is broken because the condition doesn’t account for the actual behavior of `list(APPEND)`. Running:

```cmake
set(foo "4;")
list(APPEND foo "4;")
message("${foo}")

```

outputs

```auto
4;;4;

```

so the condition should be `if(NOT "${${in_out_var}}" STREQUAL "4;;4;;4;")`.

Or we could rewrite the function to avoid this extra semicolon:

```cmake
function(simpleRecursive in_out_var)
    list(APPEND ${in_out_var} "4")
    if(NOT "${${in_out_var}}" STREQUAL "4;4;4")
        simpleRecursive(${in_out_var})
    endif()
    set(${in_out_var} ${${in_out_var}} PARENT_SCOPE)
endfunction()

set(list_of_fours "4")
simpleRecursive(list_of_fours)
message("${list_of_fours}")

```

`${${in_out_var}}` means "the value of the value of `in_out_var`". Maybe an example can help:

```cmake
function(print_variable var)
  message("${var}") # this outputs "my_letter"
  message("${${var}}") # this outputs "A"
endfunction()

set(my_letter "A")
print_variable(my_letter)

```

If you have more questions about the CMake syntax, don’t hesitate to create a new thread and to ping me on it!

---

<div class="post-metadata">

### Author: ![developer](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/d/bcef8e/32.png) [@developer](https://discourse.cmake.org/u/developer)
#### Post date: [April 23, 2020, 1:23pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/13 "2020-04-23T13:23:10Z")

</div>

Thank you so much for the help! I think understanding `${${var}}` was the missing key to my puzzle. Is that described somewhere in the CMake documentation?

For the curious, here is my updated recursive solution 🙂

```auto
function(getAllSubdirs dir dirs)
    # get subdirectories for dir
    get_property(subdirs DIRECTORY ${dir} PROPERTY SUBDIRECTORIES)
    # iterate any found subdirectories
    foreach(subdir ${subdirs})
        # append each sub directory
        list(APPEND ${dirs} ${subdir})
        getAllSubdirs(${subdir} ${dirs})
    endforeach()
    set(${dirs} ${${dirs}} PARENT_SCOPE)
endfunction()

set(Project_Directories ${CMAKE_SOURCE_DIR})
getAllSubDirs(. Project_Directories)
message("all dirs " ${Project_Directories})

```

---

<div class="post-metadata">

### Author: ![McMartin](https://discourse.cmake.org/user_avatar/discourse.cmake.org/mcmartin/32/150_2.png) [@McMartin](https://discourse.cmake.org/u/McMartin)
#### Post date: [April 23, 2020, 5:24pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/14 "2020-04-23T17:24:30Z")

</div>

> [@developer](#):
>
> Is that described somewhere in the CMake documentation?

Yes, in [https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#variable-references](https://cmake.org/cmake/help/latest/manual/cmake-language.7.html#variable-references).

---

<div class="post-metadata">

### Author: ![Francis\_Giraldeau](https://discourse.cmake.org/user_avatar/discourse.cmake.org/francis_giraldeau/32/397_2.png) [@Francis\_Giraldeau](https://discourse.cmake.org/u/Francis_Giraldeau)
#### Post date: [April 23, 2020, 9:04pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/15 "2020-04-23T21:04:05Z")

</div>

I’m using a different approach. It is more intrusive, but somewhat simple. Inside a cmake module, define a property to contain the list of targets. After using add\_executable() or add\_library(), use that macro to insert the target in the list. My use case was to generate an installation option for each target automatically. The list of targets can be used for something else I guess.

```
set_property(GLOBAL PROPERTY target_list)

# add_install_option must be a macro, if put in a function, the option does
# not exists right after being declared.
macro(add_install_option target)
    string(TOUPPER ${target} TGT)
    option(CONFIG_INSTALL_${TGT} "Install ${TGT}" ON)
    get_property(tmp GLOBAL PROPERTY target_list)
    list(APPEND tmp CONFIG_INSTALL_${TGT})
    set_property(GLOBAL PROPERTY target_list ${tmp})
endmacro()
```

---

<div class="post-metadata">

### Author: ![Lieven](https://discourse.cmake.org/letter_avatar_proxy/v4/letter/l/c37758/32.png) [@Lieven](https://discourse.cmake.org/u/Lieven)
#### Post date: [April 26, 2020, 9:11pm UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/16 "2020-04-26T21:11:07Z")

</div>

I personally think this is something cmake should be able to tell us, instead of users trying to re-invent the wheel, as as proven in this discussion it seems hard. In case makefiles are generated, make does seems to know it since it allows tab completion on the targets, also something not possible when invoking via cmake --build --target.  
I assume cmake when generating the build system has full view on it and this information is somewhere also present in the cmake cache ?  
There are many scenarios to think of which can start by just having this information, in several build automations and CI scenarios.

Could this please be provided ?

---

<div class="post-metadata">

### Author: ![dwarwick](https://discourse.cmake.org/user_avatar/discourse.cmake.org/dwarwick/32/2298_2.png) [@dwarwick](https://discourse.cmake.org/u/dwarwick)
#### Post date: [April 7, 2022, 9:18am UTC](https://discourse.cmake.org/t/cmake-list-of-all-project-targets/1077/17 "2022-04-07T09:18:40Z")

</div>

Late, but for anyone else finding this in search results, the OP’s original question can be accomplished with

```auto
function (_get_all_cmake_targets out_var current_dir)
    get_property(targets DIRECTORY ${current_dir} PROPERTY BUILDSYSTEM_TARGETS)
    get_property(subdirs DIRECTORY ${current_dir} PROPERTY SUBDIRECTORIES)

    foreach(subdir ${subdirs})
        _get_all_cmake_targets(subdir_targets ${subdir})
        list(APPEND targets ${subdir_targets})
    endforeach()

    set(${out_var} ${targets} PARENT_SCOPE)
endfunction()

# Run at end of top-level CMakeLists
_get_all_cmake_targets(all_targets ${CMAKE_CURRENT_LIST_DIR})

```
