28

Is there a way to get all targets of a CMake project from within the top level CMakeLists.txt, i.e. iterate over the targets programmatically?

The reason I want to do this is to apply some XCode specific settings to every target . .

if (CMAKE_GENERATOR MATCHES "Xcode")
    include(sanitize_xcode)
    sanitize_xcode(myTarget)
endif()

FYI - the sanitization module looks like this . .

macro (set_xcode_property TARGET XCODE_PROPERTY XCODE_VALUE)
    set_property (TARGET ${TARGET} PROPERTY XCODE_ATTRIBUTE_${XCODE_PROPERTY} ${XCODE_VALUE})
endmacro (set_xcode_property)

macro (sanitize_xcode TARGET)
    set_xcode_property(${TARGET} GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Debug] "YES")
    set_xcode_property(${TARGET} GCC_GENERATE_DEBUGGING_SYMBOLS[variant=MinSizeRel] "NO")
    set_xcode_property(${TARGET} GCC_GENERATE_DEBUGGING_SYMBOLS[variant=RelWithDebInfo] "YES")
    set_xcode_property(${TARGET} GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Release] "NO")

    set_xcode_property(${TARGET} COPY_PHASE_STRIP[variant=Debug] "NO")
    set_xcode_property(${TARGET} COPY_PHASE_STRIP[variant=MinSizeRel] "YES")
    set_xcode_property(${TARGET} COPY_PHASE_STRIP[variant=RelWithDebInfo] "NO")
    set_xcode_property(${TARGET} COPY_PHASE_STRIP[variant=Release] "YES")

    set_xcode_property(${TARGET} GCC_OPTIMIZATION_LEVEL[variant=Debug] "0")
    set_xcode_property(${TARGET} GCC_OPTIMIZATION_LEVEL[variant=MinSizeRel] "s")
    set_xcode_property(${TARGET} GCC_OPTIMIZATION_LEVEL[variant=RelWithDebInfo] "3")
    set_xcode_property(${TARGET} GCC_OPTIMIZATION_LEVEL[variant=Release] "3")

    set_xcode_property(${TARGET} IPHONEOS_DEPLOYMENT_TARGET[variant=Debug] "7.0")
    set_xcode_property(${TARGET} IPHONEOS_DEPLOYMENT_TARGET[variant=MinSizeRel] "7.0")
    set_xcode_property(${TARGET} IPHONEOS_DEPLOYMENT_TARGET[variant=RelWithDebInfo] "7.0")
    set_xcode_property(${TARGET} IPHONEOS_DEPLOYMENT_TARGET[variant=Release] "7.0")
endmacro (sanitize_xcode)
roalz
  • 2,699
  • 3
  • 25
  • 42
learnvst
  • 15,455
  • 16
  • 74
  • 121
  • Do you mean iterating the targets in your cmake script programmatically? – Tamás Szelei May 25 '16 at 11:19
  • To what purpose? (Smelling a X-Y problem.) Whatever you want to do is probably best done in CMakeLists.txt itself. – DevSolar May 25 '16 at 11:33
  • 2
    @DevSolar see edits. – learnvst May 25 '16 at 11:38
  • Can you please also give the code for `sanitize_xcode()`? Do you add `POST_BUILD` steps or do you just add compiler options? If the later, there won't be the need to iterate over all targets. – Florian May 25 '16 at 12:42
  • Thanks. Just looking at [`XCODE_ATTRIBUTE_`](https://cmake.org/cmake/help/latest/prop_tgt/XCODE_ATTRIBUTE_an-attribute.html) target property wouldn't it be enough to set the equivalent [`CMAKE_XCODE_ATTRIBUTE_`](https://cmake.org/cmake/help/latest/variable/CMAKE_XCODE_ATTRIBUTE_an-attribute.html#variable:CMAKE_XCODE_ATTRIBUTE_) variables at root level to be applied to all targets? – Florian May 25 '16 at 14:01

3 Answers3

36

In correction to Florian's answer, BUILDSYSTEM_TARGETS is a not really a global property but a directory scoped one. A request for enhancement is currently open to request a truly global property. Using SUBDIRECTORIES property it's possible retrieve recursively all targets in the scope of the current directory with the following function:

function(get_all_targets var)
    set(targets)
    get_all_targets_recursive(targets ${CMAKE_CURRENT_SOURCE_DIR})
    set(${var} ${targets} PARENT_SCOPE)
endfunction()

macro(get_all_targets_recursive targets dir)
    get_property(subdirectories DIRECTORY ${dir} PROPERTY SUBDIRECTORIES)
    foreach(subdir ${subdirectories})
        get_all_targets_recursive(${targets} ${subdir})
    endforeach()

    get_property(current_targets DIRECTORY ${dir} PROPERTY BUILDSYSTEM_TARGETS)
    list(APPEND ${targets} ${current_targets})
endmacro()

get_all_targets(all_targets)
message("All targets: ${all_targets}")
ceztko
  • 14,736
  • 5
  • 58
  • 73
  • 7
    Oh god, I love you, this solved a problem that stuck me all day. +1 – Sprite Mar 28 '21 at 19:52
  • 1
    This is so great, I had been wanting a way to do this for ages, but had basically given up – Andrew W Apr 22 '21 at 05:55
  • 2
    It should be noted that this solution will produce a list of really **all** targets including those that have been expicitly excluded from "all" using: `add_executable(some_target EXCLUDE_FROM_ALL some_target.c)` This is really useful for adding a target that will build **really all**, including even the excluded targets: `add_custom_target(really_all DEPENDS ${all_targets})` – n0dus Feb 25 '22 at 11:12
7

Turning my comment into an answer

To have a list of all targets is a wish that has been out there for a while, but the global property TARGETS is not yet implemented (as for May-2016, see "Listing all targets" discussion).

Edit: It is now implemented: Global BUILDSYSTEM_TARGETS property was released with CMake 3.7

So you can implement this yourself using CMake script itself - as @DevSolar as commented/answered or like here - but I've learned over the time working with CMake that you could also change a lot of target properties globally. E.g. most target properties are defaulted to an equivalent global variable setting.

You can take advantage of this in your case and solve this by adding the following to your global CMakeLists.txt file:

set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Debug] "YES")
set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=MinSizeRel] "NO")
set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=RelWithDebInfo] "YES")
set(CMAKE_XCODE_ATTRIBUTE_GCC_GENERATE_DEBUGGING_SYMBOLS[variant=Release] "NO")

set(CMAKE_XCODE_ATTRIBUTE_COPY_PHASE_STRIP[variant=Debug] "NO")
set(CMAKE_XCODE_ATTRIBUTE_COPY_PHASE_STRIP[variant=MinSizeRel] "YES")
set(CMAKE_XCODE_ATTRIBUTE_COPY_PHASE_STRIP[variant=RelWithDebInfo] "NO")
set(CMAKE_XCODE_ATTRIBUTE_COPY_PHASE_STRIP[variant=Release] "YES")

set(CMAKE_XCODE_ATTRIBUTE_GCC_OPTIMIZATION_LEVEL[variant=Debug] "0")
set(CMAKE_XCODE_ATTRIBUTE_GCC_OPTIMIZATION_LEVEL[variant=MinSizeRel] "s")
set(CMAKE_XCODE_ATTRIBUTE_GCC_OPTIMIZATION_LEVEL[variant=RelWithDebInfo] "3")
set(CMAKE_XCODE_ATTRIBUTE_GCC_OPTIMIZATION_LEVEL[variant=Release] "3")

set(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET[variant=Debug] "7.0")
set(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET[variant=MinSizeRel] "7.0")
set(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET[variant=RelWithDebInfo] "7.0")
set(CMAKE_XCODE_ATTRIBUTE_IPHONEOS_DEPLOYMENT_TARGET[variant=Release] "7.0")

References

Florian
  • 39,996
  • 9
  • 133
  • 149
  • 2
    No, global `BUILDSYSTEM_TARGETS` has not been added, it's a directory property. There's an [rfe](https://gitlab.kitware.com/cmake/cmake/-/issues/20124#) for that – ceztko Jun 10 '20 at 17:29
  • 2
    Using `BUILDSYSTEM_TARGETS` and `SUBDIRECTORIES` it's possible to recursively retrieve targets defined at the current directory scope. See my [answer](https://stackoverflow.com/a/62311397/213871). – ceztko Jun 10 '20 at 19:17
2

If you want to go over a list of targets, set your CMakeLists.txt up to provide that list in the first place.

if ( CMAKE_GENERATOR MATCHES "Xcode" )
    include(sanitize_xcode)
endif()

# A list of executables to build
set( project_EXECUTABLES
     foo
     bar
   )

# List of sources for each executable, following some naming scheme

# foo
set( EXE_foo_SOURCES
     foo/main.c
   )

# bar
set( EXE_bar_SOURCES
     bar/main.c
   )

# For each executable in the list...
foreach( exe ${project_EXECUTABLES} )
    # declare the target...
    add_executable( ${exe} ${EXE_${exe}_SOURCES} )

    # ...and do whatever additional configuration you need
    if ( CMAKE_GENERATOR MATCHES "Xcode" )
        sanitize_xcode( ${exe} )
    endif()
endforeach()
DevSolar
  • 67,862
  • 21
  • 134
  • 209
  • I found it generally a good trick to have a series of `set()` commands at the beginning, setting everything up, and doing all the "magic" at the end. Actually, [in my re-useable CMake framework](http://jaws.rootdirectory.de), I *only* do `set()`s in my `CMakeLists.txt`, and then `include()` a file with all the "magic". If I add / change source files, I only "touch" the `CMakeLists.txt`, if I modify the "magic" I "touch" a different file. Easier to maintain, IMHO. – DevSolar May 25 '16 at 11:49
  • This is not an answer since the list of targets may just be unknown/unpredictable. – ceztko Aug 19 '20 at 11:26
  • @ceztko: 1) If your list of targets is unpredictable, you have other problems. 2) The answer *shows a way* to have a list of targets set up to iterate over. 3) `BUILDSYSTEM_TARGETS` was introduced half a year **after** Florian and I wrote our (initial) answers, so you can hardly blame us for not making use of it when we wrote our answers. At the time, these **were** the answers to the question. The later appearance of better mechanics might make this answer a bit obsolete, but "not an answer" is uncalled for. – DevSolar Aug 19 '20 at 12:03
  • @ceztko: Oh, I think I get it now. You're having CMakeLists.txt in *subdirectories* as well, and that's why you consider the list of targets to be "unknown / unpredictable"? Well, I don't do that, coming from the "recursive make considered harmful" school of thought, having only one top-level CMakeLists.txt. So we're working from different assumptions here. – DevSolar Aug 19 '20 at 18:54