2

In CMake projects with multiple targets, I often find myself repeating some code over and over again. See this example:

cmake_minimum_required(VERSION 3.12)
project(rapid_benchmark)

set(CMAKE_CXX_STANDARD 17)

add_executable(benchmark main.cpp)
add_executable(benchmark_parallel main.cpp) # enable parallel processing
add_executable(benchmark_openmp main.cpp) # enable openmp
add_executable(benchmark_static main.cpp) # enable static optimizations
# ... more targets

# Adding basic dependecies
find_package(abc)
if(abc_FOUND)
    target_link_libraries(benchmark abc::abc)
    target_link_libraries(benchmark_parallel abc::abc)
    target_link_libraries(benchmark_openmp abc::abc)
    # ... all other targets ...
    # The same for the includes etc.

find_package(xyz)
if(xyz_FOUND)
    target_link_libraries(benchmark xyz::xyz)
    # ... all other targets ...

This is annoying and error prone, especially when adding new targets.

How can I avoid repetitive code with multiple targets in a CMake project? For example, is there a way to put the targets into a kind of list and call target_link_libraries on that list?

Stanislav Pankevich
  • 11,044
  • 8
  • 69
  • 129
florestan
  • 4,405
  • 2
  • 14
  • 28

3 Answers3

3

Same as Bernhard said, but in CMake :)

macro(add_benchmark)
  set(singleValue NAME)
  set(multipleValues SOURCES)
  cmake_parse_arguments(local "" "${singleValue}" "${multipleValues}" ${ARGN})

  set (libraries)
  if (abc_FOUND)
    set (libraries ${libraries} abc::abc)
  endif()
  if (xyz_FOUND)
    set (libraries ${libraries} xyz::xyz)
  endif()

  add_executable(${local_NAME} ${local_SOURCES})
  target_link_libraries(${local_NAME} ${libraries})
endmacro()

Then you can simply call it as any other CMake command:

add_benchmark(
  NAME benchmark
  SOURCES main.cpp
)

add_benchmark(
  NAME benchmark_other
  SOURCES main.cpp other.cpp
)
AlexDenisov
  • 4,022
  • 25
  • 31
  • 1
    I'd remove the libraries variable and execute the target_link_libraries within the if's. This will save you two lines and improve clarity. – Bernhard Dec 20 '18 at 13:26
  • @Bernhard matter of taste :) I would go as far as defining another macro/function that would get me back the list of available libraries, and then link against those. – AlexDenisov Dec 20 '18 at 18:08
  • 1
    better to use a `function()` instead of `macro()` so all you variable in each `set()` are scoped to the function body... – Mizux Dec 21 '18 at 15:15
2

I would consider to create a macro, that you can call for each application, which does the both the compilation and the linking

You will probably find that you want to use cmake-parse-arguments

Bernhard
  • 3,619
  • 1
  • 25
  • 50
1

Something like this... https://cmake.org/cmake/help/latest/command/foreach.html

set(targets a b c d e)
foreach(loop_var IN LISTS targets)
    add_executable($loop_var main.cpp)
endforeach(loop_var)

Combined with a macro that does a lot of stuff will keep it manageable.

Further details: Looping over a string list

Connor Clark
  • 671
  • 7
  • 15
fdk1342
  • 3,274
  • 1
  • 16
  • 17