-1

I am trying to collect a list of all of the executables my project generates and create a make target that runs all of the binaries.

Here's an example project structure:

.
├── CMakeLists.txt
├── cmake
│   └── collect_binaries.cmake
└── src
    ├── CMakeLists.txt
    ├── a
    │   ├── CMakeLists.txt
    │   └── a.cpp
    └── b
        ├── CMakeLists.txt
        └── b.cpp

My top level CMakeLists.txt file is below. As you can see, I'm setting a global list which I would like to use.

cmake_minimum_required(VERSION 3.15)

project(
  example
  VERSION 0.0.0
  LANGUAGES CXX
)

include(cmake/collect_binaries.cmake)

set(EXECUTABLES_LIST)
add_subdirectory(src)

add_custom_target(run_all ALL
  COMMAND ${CMAKE_COMMAND} -E echo "Running all executables..."
)

message(STATUS "executables: ${EXECUTABLES_LIST}")

# Add performance executables as dependencies for the custom target
add_dependencies(run_all ${EXECUTABLES_LIST})

foreach(exe IN LISTS ${EXECUTABLES_LIST})
  add_custom_command(
    TARGET run_all POST_BUILD
    COMMAND ${CMAKE_COMMAND} -E echo "Running ${exe}..."
    COMMAND $<TARGET_FILE:${exe}>
  )
endforeach()

I intend to collect all my targets by wrapping the call to add_executable with my own function, defined in cmake/collect_binaries.cmake:

function(collect target_name)
  add_executable(${target_name} ${ARGN})

  set(${EXECUTABLES_LIST} "${EXECUTABLES_LIST}" "${target_name}" PARENT_SCOPE)
endfunction()

Here are the rest of my files:

src/CMakeLists.txt:

add_subdirectory(a)
add_subdirectory(b)

src/a/CMakeLists.txt:

collect(
  a a.cpp
)

src/b/CMakeLists.txt:

collect(
  b b.cpp
)

both a and b.cpp:

#include <iostream>

int main() {
  std::cout << "here" << std::endl;
  return 0;
}

When I run this:

mkdir build
cd build
cmake ..

I can see that the executables are not appended to the list:

:: cmake ..
-- The CXX compiler identification is AppleClang 14.0.0.14000029
-- Detecting CXX compiler ABI info
-- Detecting CXX compiler ABI info - done
-- Check for working CXX compiler: /Applications/Xcode.app/Contents/Developer/Toolchains/XcodeDefault.xctoolchain/usr/bin/c++ - skipped
-- Detecting CXX compile features
-- Detecting CXX compile features - done
-- executables: 
CMake Error at CMakeLists.txt:21 (add_dependencies):
  add_dependencies called with incorrect number of arguments


-- Configuring incomplete, errors occurred!
K. Shores
  • 875
  • 1
  • 18
  • 46
  • After writing this, I realized that I could just use ctest to collect all of these and have ctest run everything. However, I would appreciate an answer to my question. – K. Shores Jul 31 '23 at 18:33
  • Why not just add tests? – KamilCuk Jul 31 '23 at 18:37
  • well, yes that's the comment I added above... But the reason I didn't initially is because these aren't necessarily tests. Also, I intend to do other things that I can't do with add_test. I want to call the binaries all at once, but also I want to add a custom command that will do something with all of the executables – K. Shores Jul 31 '23 at 18:45
  • 1
    [How do I iterate over all CMake targets programmatically?](https://stackoverflow.com/q/37434946/11107541) – starball Aug 01 '23 at 00:44

1 Answers1

1

Iterate over all subdirectories and iterate over all targets and check if the target is a executable then add it to a list. And output.

function(get_all_executables out)
  if(NOT DEFINED ${out})
    set(${out})
  endif()
  if(ARGC EQUAL 2)
    set(root ${ARGV1})
  else()
    set(root ${CMAKE_SOURCE_DIR})
  endif()
  #
  get_directory_property(targets BUILDSYSTEM_TARGETS DIRECTORY ${root})
  foreach(target IN LISTS targets)
    get_target_property(type ${target} TYPE)
    if(type STREQUAL "EXECUTABLE")
      list(APPEND ${out} ${target})
    endif()
  endforeach()
  get_directory_property(dirs SUBDIRECTORIES DIRECTORY ${root})
  foreach(dir IN LISTS dirs)
    get_all_executables(${out} ${dir})
  endforeach()
  set(${out} ${${out}} PARENT_SCOPE)
endfunction()
get_all_executables(list)
message(FATAL_ERROR "EXECUTABLES:" ${list})
KamilCuk
  • 120,984
  • 8
  • 59
  • 111