Skip to content

CMake option name clashes with FetchContent #4618

@jngrad

Description

@jngrad

This is a follow-up to the dev-meeting discussion about name clashes when including external libraries with FetchContent.

CMake target name clashes

ESPResSo CMake target names cannot clash with included libraries thanks to the use of a unique prefix:

add_library(espresso_core SHARED)
add_library(espresso::core ALIAS espresso_core)
target_sources(espresso_core PRIVATE cells.cpp ...)
target_link_libraries(espresso_core PRIVATE espresso::config ...)
install(TARGETS espresso_core
        LIBRARY DESTINATION ${ESPRESSO_INSTALL_PYTHON}/espressomd)

Advantages:

  • no name clashes for shared objects: files always prefixed by espresso_
    • this was also necessary for Cython: shapes.py no longer clashes with shapes.so (renamed to espresso_shapes.so)
  • no name clashes for CMake targets: targets always prefixed by espresso_
    • all modifiers have to use the espresso_ target: target_sources(), target_link_libraries(), install()
    • all consumers have to use the espresso:: alias, including any parent CMake project
  • if a consumer tries to link against a non-existing espresso::target_name, CMake throws an error; linking against a non-existing espresso_target_name doesn't throw an error, instead the string is passed to the linker, which may or may not throw an error

CMake project option clashes

Regarding CMake project options, to my knowledge there isn't a built-in way to namespace them. Contrary to ExternalProject, which provides CMAKE_ARGS and CMAKE_CACHE_ARGS to pass CMake options to the external CMake project, FetchContent doesn't allow providing different CMake options, because the CMakeCache.txt of the subproject is merged into the root project. There have been several discussions about improving FetchContent, see discussions in cmake/cmake#23710 (comment) and cmake/cmake#22687, but none of them addresses this situation.

There are several workarounds.

The first one consists in defining a subset of common CMake options that can be safely inherited from a parent project, and automatically disable all other options. The MWE below is adapted from
Preparing libraries for CMake FetchContent by Jonathan:

project(ESPRESSO LANGUAGES CXX)

# define common build options that are inherited from parent projects
option(WITH_COVERAGE "Enable code coverage instrumentation" OFF)
add_subdirectory(src)

# define build options specific to this project, that
# cannot be activated if this project is a subproject
if(CMAKE_CURRENT_SOURCE_DIR STREQUAL CMAKE_SOURCE_DIR)
  # we're in the root, define additional targets for developers.
  option(ESPRESSO_WITH_TESTS      "Enable tests" ON)
  if(ESPRESSO_WITH_TESTS)
    add_subdirectory(tests)
  endif()
endif()

The downside is, some project-specific options shouldn't be hidden, e.g. WITH_HDF5, but at the same time they may clash with parent projects.

The second workaround consists in setting the CMake options before the add_subdirectory() command, while taking care of stashing the root project variables and unstashing them later with the same properties (cache, docstring) to avoid issues with variable lifetime and ccmake, and making sure these properties are always manually updated whenever the original option declarations change. The MWE below is adapted from Using FetchContent_Declare together with CMAKE_ARGS by Tsyvarev:

if(NOT librepa_POPULATED)
  FetchContent_Populate(librepa)
  set(WITH_TESTS_OLD ${WITH_TESTS})
  set(WITH_TESTS OFF CACHE INTERNAL "")
  add_subdirectory(${librepa_SOURCE_DIR} ${librepa_BINARY_DIR})
  set(WITH_TESTS ${WITH_TESTS_OLD} CACHE BOOL "Enable tests" FORCE)
endif()

The third method consists in manually prefixing all CMake options with the project name, i.e.

cmake .. -D ESPRESSO_WITH_HDF5=ON -D ESPRESSO_WITH_TESTS=ON

This is the solution taken by the waLBerla project.

In my opinion, the third method is the safest and most sustainable strategy. We would need to add a prefix to our own CMake options to avoid name collisions, and rely on these options instead of e.g. FFTW_FOUND variables that can be inherited from a parent project that includes ESPResSo. We should also convince library developers to do the same in their projects (SC-SGS/repa#51, walberla/walberla#191).

Originally posted by @jngrad in #4474 (comment)

Metadata

Metadata

Assignees

Labels

Type

No type

Projects

No projects

Milestone

No milestone

Relationships

None yet

Development

No branches or pull requests

Issue actions