Crosspost: CMake Import/Export

I have created four example projects for demonstrating several aspects of import/export with cmake. Description is here: https://habla.news/u/purplekarrot.net/cmake-import-export

Most beman libraries are header only, but you may find some information useful nevertheless.

Definitely a few interesting points there:

Note that Qux does not set any value that begins with CMAKE_. Those variables are not meant to be set by projects. Setting them as cache variables would leak into the parent project, while setting them as non-cache variables would prevent injection from the parent project or from the packager.

This seems to be a fundamental point about cmake usage that is never been communicated clearly – although even clear communication about cmake has been obsoleted so it’s difficult to follow best practices. For Beman this should probably be in our guidelines.

Consider a C++ library that requires C++23 internally, but it has a C API, so C++23 is not a usage requirement.

I don’t think there’s anything in c++23 that would be abi incompatible with earlier versions – maybe contracts leaks in for 26, but I’m not even sure on that.

I see only c code? With CXX_MODULES there are also many points to know.

i.e.: C++20 Modules, CMake, And Shared Libraries - Crascit

And CXX_MODULES can’t be an interface library today!

see Linker error with CXX_MODULES - Development - CMake Discourse

That is not always true, i.e. from scope

# Modules opt in only on compilers that support it: msvc, g++-15 and clang-20+
if (CMAKE_CXX_COMPILER_ID STREQUAL "Clang" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 20)
    set(CMAKE_CXX_SCAN_FOR_MODULES 1)
elseif (CMAKE_CXX_COMPILER_ID STREQUAL "GNU" AND CMAKE_CXX_COMPILER_VERSION VERSION_GREATER_EQUAL 15)
    set(CMAKE_CXX_SCAN_FOR_MODULES 1)
elseif(MSVC)
    set(CMAKE_CXX_SCAN_FOR_MODULES 1)
else()
    set(CMAKE_CXX_SCAN_FOR_MODULES 0)
endif()

set(CMAKE_EXPORT_COMPILE_COMMANDS ON)
set(CMAKE_CXX_VISIBILITY_PRESET hidden)
set(CMAKE_VISIBILITY_INLINES_HIDDEN TRUE)

or from Professional CMake book

  set(stageDir ${CMAKE_CURRENT_BINARY_DIR}/stage)
  include(GNUInstallDirs)
  if(NOT CMAKE_RUNTIME_OUTPUT_DIRECTORY)
      set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${stageDir}/${CMAKE_INSTALL_BINDIR})
  endif()
  if(NOT CMAKE_LIBRARY_OUTPUT_DIRECTORY)
      set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${stageDir}/${CMAKE_INSTALL_LIBDIR})
  endif()
  if(NOT CMAKE_ARCHIVE_OUTPUT_DIRECTORY)
      set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${stageDir}/${CMAKE_INSTALL_LIBDIR})
  endif()


  if(PROJECT_IS_TOP_LEVEL)
      set(CMAKE_EXPORT_COMPILE_COMMANDS TRUE)
      find_program(CLANG_TIDY_EXECUTABLE clang-tidy REQUIRED)
      find_program(RUN_CLANG_TIDY_EXECUTABLE run-clang-tidy REQUIRED)
      add_custom_target(run-clang-tidy
          COMMAND ${RUN_CLANG_TIDY_EXECUTABLE}
              -clang-tidy-binary ${CLANG_TIDY_EXECUTABLE}
              -p ${CMAKE_BINARY_DIR}
  ) 
  endif()

and

cmake_minimum_required(VERSION 3.21)
  project(coverage_example LANGUAGES CXX)
  enable_testing()
  if(PROJECT_IS_TOP_LEVEL AND CMAKE_CXX_COMPILER_ID MATCHES "GNU|Clang")
      set(CMAKE_CXX_FLAGS_COVERAGE "-g -O0 --coverage")
      if(CMAKE_CXX_COMPILER_ID STREQUAL "GNU")
          string(APPEND CMAKE_CXX_FLAGS_COVERAGE " -fprofile-abs-path")
      endif()
      set(CMAKE_EXE_LINKER_FLAGS_COVERAGE "--coverage")
      set(CMAKE_SHARED_LINKER_FLAGS_COVERAGE "--coverage")
      set(CMAKE_MODULE_LINKER_FLAGS_COVERAGE "--coverage")
  endif()
  add_subdirectory(src)

Yes – my comment in August was me learning more about cmake best practices. Beman has taught me a lot about cmake I didn’t know.

Note that on my current project (and past ones for that matter) we violate these guidelines. In part because we didn’t realize and more importantly correctness and consistency requires the solution to be 100% in configuration management - reproducable without command options. We don’t want individual developers picking other C++ standards or whatever. It’s a completely opposite context to Beman an it’s goals.