How do I iterate over all CMake targets programmatically?
Asked Answered
J

4

31

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)
Jeuz answered 25/5, 2016 at 10:52 Comment(5)
Do you mean iterating the targets in your cmake script programmatically?Bobbybobbye
To what purpose? (Smelling a X-Y problem.) Whatever you want to do is probably best done in CMakeLists.txt itself.Foreplay
@Foreplay see edits.Jeuz
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.Rousseau
Thanks. Just looking at XCODE_ATTRIBUTE_<an-attribute> target property wouldn't it be enough to set the equivalent CMAKE_XCODE_ATTRIBUTE_<an-attribute> variables at root level to be applied to all targets?Rousseau
R
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

Rousseau answered 25/5, 2016 at 19:48 Comment(2)
No, global BUILDSYSTEM_TARGETS has not been added, it's a directory property. There's an rfe for thatLucky
Using BUILDSYSTEM_TARGETS and SUBDIRECTORIES it's possible to recursively retrieve targets defined at the current directory scope. See my answer.Lucky
L
40

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}")
Lucky answered 10/6, 2020 at 19:14 Comment(3)
Oh god, I love you, this solved a problem that stuck me all day. +1Fdic
This is so great, I had been wanting a way to do this for ages, but had basically given upRedmon
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})Hooch
R
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

Rousseau answered 25/5, 2016 at 19:48 Comment(2)
No, global BUILDSYSTEM_TARGETS has not been added, it's a directory property. There's an rfe for thatLucky
Using BUILDSYSTEM_TARGETS and SUBDIRECTORIES it's possible to recursively retrieve targets defined at the current directory scope. See my answer.Lucky
F
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()
Foreplay answered 25/5, 2016 at 11:40 Comment(4)
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, 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.Foreplay
This is not an answer since the list of targets may just be unknown/unpredictable.Lucky
@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.Foreplay
@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.Foreplay
B
0

Based on @ceztko's answer above, here is a version of the macro that excludes interface and custom targets:

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)
    foreach(t ${current_targets})
        get_target_property(target_type ${t} TYPE)
        if (NOT ${target_type} STREQUAL "INTERFACE_LIBRARY" AND NOT ${target_type} STREQUAL "UTILITY")
            list(APPEND ${targets} ${t})
        endif()
    endforeach()
endmacro()

I've found this useful for applying some disable warnings flags to all targets, after they had each already been created with their default warnings sets.

Behemoth answered 19/7 at 16:29 Comment(0)

© 2022 - 2024 — McMap. All rights reserved.