Screenplay: Function Wrapping add_library()

Streamline Build-Spaghetti: How Want It?

  • Screenplay: Public And Private Include Directories was a massacre

  • toolcase/base/CMakeLists.txt has become rather cluttered (base ist the worst, the others are ugly too)

  • Wanted: simple call to my_add_library(), beautifully reflecting the situation

  • Implicitly dictating the directory structure

    .
    ├── include
    │   ├── private
    │   └── public
    └── src
    
my_add_library(
  NAME base

  DEBUG

  PUBLIC_HEADERS

    sensor.h
    sensor-const.h
    sensor-random.h
    sensor-avg.h
    sensor-w1.h
    switch.h
    sysfs-switch.h
    hysteresis.h

  PRIVATE_HEADERS

    file-util.h

  SOURCES

    sensor-const.cpp
    sensor-random.cpp
    sensor-avg.cpp
    sensor-w1.cpp
    sysfs-switch.cpp
    hysteresis.cpp
    file-util.cpp
 )

Function my_add_library(): cmake_parse_arguments()

  • In same CMakeLists.txt, define function

  • cmake_parse_arguments()

    function(my_add_library)
      cmake_parse_arguments(MY_ADD_LIBRARY "SHARED;STATIC" "NAME" "PRIVATE_HEADERS;PUBLIC_HEADERS;SOURCES" ${ARGN})
    
      message("MY_ADD_LIBRARY_SHARED: >${MY_ADD_LIBRARY_SHARED}<")
      message("MY_ADD_LIBRARY_STATIC: >${MY_ADD_LIBRARY_STATIC}<")
      message("MY_ADD_LIBRARY_NAME: >${MY_ADD_LIBRARY_NAME}<")
      message("MY_ADD_LIBRARY_PRIVATE_HEADERS: >${MY_ADD_LIBRARY_PRIVATE_HEADERS}<")
      message("MY_ADD_LIBRARY_PUBLIC_HEADERS: >${MY_ADD_LIBRARY_PUBLIC_HEADERS}<")
      message("MY_ADD_LIBRARY_SOURCES: >${MY_ADD_LIBRARY_SOURCES}<")
    endfunction()
    
  • Hmm … how about error checking? SHARED and STATIC passed?

  • Externalize into my_add_library.cmake, and develop there (in script mode, mostly)

  • ⟶ we will use it in other modules too

Function my_add_library(): Final Version

function(my_add_library)
  cmake_parse_arguments(MY_ADD_LIBRARY "SHARED;STATIC;DEBUG" "NAME" "PRIVATE_HEADERS;PUBLIC_HEADERS;SOURCES" ${ARGN})

  if (MY_ADD_LIBRARY_DEBUG)
    message("MY_ADD_LIBRARY_SHARED: >${MY_ADD_LIBRARY_SHARED}<")
    message("MY_ADD_LIBRARY_STATIC: >${MY_ADD_LIBRARY_STATIC}<")
    message("MY_ADD_LIBRARY_NAME: >${MY_ADD_LIBRARY_NAME}<")
    message("MY_ADD_LIBRARY_PRIVATE_HEADERS: >${MY_ADD_LIBRARY_PRIVATE_HEADERS}<")
    message("MY_ADD_LIBRARY_PUBLIC_HEADERS: >${MY_ADD_LIBRARY_PUBLIC_HEADERS}<")
    message("MY_ADD_LIBRARY_SOURCES: >${MY_ADD_LIBRARY_SOURCES}<")
    message("ARGC: ${ARGC}")
    message("ARGV: ${ARGV}")
    message("ARGN: >${ARGN}<")
  endif()

  if (MY_ADD_LIBRARY_SHARED AND MY_ADD_LIBRARY_STATIC)
    message(FATAL_ERROR "STATIC *and* SHARED is not possible")
  endif()

  foreach (f ${MY_ADD_LIBRARY_PUBLIC_HEADERS})
    list(APPEND public_headers "include/public/${f}")
  endforeach()
  foreach (f ${MY_ADD_LIBRARY_PRIVATE_HEADERS})
    list(APPEND public_headers "include/private/${f}")
  endforeach()
  foreach (f ${MY_ADD_LIBRARY_SOURCES})
    list(APPEND sources "src/${f}")
  endforeach()

  if (MY_ADD_LIBRARY_SHARED)
    set(how "SHARED")
  elseif (MY_ADD_LIBRARY_STATIC)
    set(how "STATIC")
  endif()

  add_library(
    ${MY_ADD_LIBRARY_NAME}
    ${how}
    ${public_headers}
    ${private_headers}
    ${sources}
  )

  target_include_directories(${MY_ADD_LIBRARY_NAME} PUBLIC ./include/public)
  target_include_directories(${MY_ADD_LIBRARY_NAME} PRIVATE ./include/private)

  install(TARGETS ${MY_ADD_LIBRARY_NAME} DESTINATION lib)
  install(FILES ${MY_ADD_LIBRARY_PUBLIC_HEADERS} DESTINATION include)
   
endfunction()

Make my_add_library() A Matter For The Architect

  • Put in toplevel cmake/ directory (for all files of similar purpose)

  • ⟶ add to ${CMAKE_MODULE_PATH}

  • include() in toplevel CMakeLists.txt

    • Remove existing include() from base/CMakeLists.txt

    • RANT: when including via ${CMAKE_MODULE_PATH}, omit the .cmake extension or the file will not be found

  • directory scope (see Variables)

  • Restructure data-logger and boiling-pot

  • While we are at it, move compiler settings into /cmake/compiler.cmake