Build system internals

This page provides details about the CMake build system. Files processed by the top level CMakeLists.txt script are listed in the TOC in chronological order.

  1. Coding convention
  2. Configuration
    1. ./CMakeLists.txt and ./cmake/setup_*.cmake
    2. ./cmake/checks/check_*.cmake
    3. ./cmake/modules/Find*.cmake
    4. ./cmake/configure/configure_*.cmake
    5. Global variables controlling the build process
    6. The unity build subsystem
  3. Target definition and installation
    1. ./include/deal.II/base/config.h.in
    2. ./source/CMakeLists.txt
    3. ./cmake/config/CMakeLists.txt

Coding convention

Coding conventions are always a matter of choice. Nevertheless, the following rules should be considered:

An example:
foreach(_build ${DEAL_II_BUILD_TYPES})
  #
  # Set an appropriate keyword depending on target and build type:
  #
  if(NOT "${CMAKE_BUILD_TYPE}" STREQUAL "DebugRelease")
    set(_keyword "general")
  else()
    if(_build MATCHES DEBUG)
      set(_keyword "debug")
    else()
      set(_keyword "optimized")
    endif()
  endif()
endforeach()
list(APPEND CONFIG_LIBRARIES
  ${_keyword}
  ${CONFIG_LIBRARIES_${_build}}
  )

set_target_properties(${DEAL_II_TARGET_NAME}_${build_lowercase}
  PROPERTIES
  VERSION ${VERSION}
  SOVERSION ${VERSION}
  LINK_FLAGS "${DEAL_II_LINKER_FLAGS_${build}}"
  COMPILE_DEFINITIONS "${DEAL_II_DEFINITIONS};${DEAL_II_DEFINITIONS_${build}}"
  COMPILE_FLAGS "${DEAL_II_CXX_FLAGS_${build}}"
  )

CMake operates almost always with variables in global state. To guard against accidental overwrite of variables the following naming conventions must be followed at all times:

Configuration

./CMakeLists.txt and ./cmake/setup_*.cmake

The very first configuration steps after some initial setup in ./CMakeLists.txt takes place in some ./cmake/setup_*.cmake files:

./cmake/checks/check_*.cmake

The next step in the configuration process is to include all checks residing under ./cmake/checks. Currently there are (included and executed in alphabetical order):

./cmake/checks/check_01_cpu_features.cmake
  - Platform introspection for CPU features goes here and must be
    guarded with DEAL_II_ALLOW_PLATFORM_INTROSPECTION

./cmake/checks/check_01_cxx_features.cmake
  - Check for supported C++ language features such as sufficient C++17
    support

./cmake/checks/check_02_compiler_features.cmake
  - Search for support for compiler dependent features such as stack
    trace support, demangler support, etc.

./cmake/checks/check_02_system_features.cmake
  - Checks for specific platform (Linux/Darwin/CYGWIN/Windows..)
    features and support

./cmake/checks/check_03_compiler_bugs.cmake
  - Check for compiler bugs

./cmake/modules/Find*.cmake

These are find modules for the configure_*.cmake files and the CONFIGURE_FEATURE macro as will explained later. It is crucial that a find module behaves correctly. Therefore, the following rules are mandatory:

./cmake/configure/configure_*.cmake

The final step in the configuration phase is the setup of features (which refer to external or bundled libraries deal.II can optionally interface with.)

At bare minimum configure_<feature>.cmake file for a feature just consists of a call to the configure_feature(<FEATURE>) macro which is implemented in ./cmake/macros/macro_configure_feature.cmake. In this case the corresponding Find<FEATURE>.cmake module is used to determine whether an external dependency can be resolved or not. Depending on the current state of DEAL_II_WITH_<FEATURE> (see here) the configuration variables

FEATURE_LIBRARIES
FEATURE_LIBRARIES(|_DEBUG|_RELEASE)
FEATURE_(|BUNDLED_)INCLUDE_DIRS
FEATURE_LINKER_FLAGS(|_DEBUG|_RELEASE)
FEATURE_CXX_FLAGS(|_DEBUG|_RELEASE)
FEATURE_DEFINITIONS(|_DEBUG|_RELEASE)
are appended to the set of global variables and DEAL_II_WITH_<FEATURE> is set to TRUE.

It is possible to override this default behaviour with the following variables and macros (all of them are optional and will be replaced by an appropriate default action if unset):

Global variables controlling the build process

The following list describes all global variables controlling the build process and the visibility associated with it (internal use for compiling deal.Ii, externally used variables will get exported in deal.IIConfig.cmake). Lists should be manipulated with LIST(APPEND ...), flags with ADD_FLAGS(...) (or if it is necessary to guard them with ENABLE_IF_SUPPORTED(...).)

Feature configuration must not be added directly to this variables but to corresponding <FEATURE>_* variables, instead. Feature configuration variables get appended to the below list of global configuration variables automatically.

The unity build subsystem

For a general description of this feature see the deal.II Readme entry. Many of the various CMakeLists.txt files split their source files into two lists: one list of files that are relatively cheap to compile, which are concatenated into the unity build files, and a list of files that are more expensive to compile, which are not included in the unity files. For example: most of the finite element classes take about 5-10 seconds to compile while the FEValues instantiation files each take about 60 seconds. In addition, many CMakeLists.txt files define a variable _n_includes_per_unity_file which specifies how many files should be concatenated into each unity file.

A disadvantage to this approach is that it requires profiling the build in two places: the time and memory usage of all source files must be measured and the variable _n_includes_per_unity_file should be set so that the unity build files are not overly expensive in terms of memory and wall time. The current values were chosen so that the unity files, with GCC, require about 30 to 60 seconds of wall time and about 2 GB of memory.

If you want to add a new file to one of the CMakeLists.txt files then you should place it in either the _unity_include_src or the _separate_src list (not both). If the new file only takes a few seconds to compile then it should be placed in the former category (so that it may be built in a unity file) and otherwise it should be in the latter. If you are not sure where a new file belongs then placing it in the _separate_src list is the conservative choice.

Target definition and installation

./include/deal.II/base/config.h.in

In contrast to autoconf there is no intermediate step any more that automatically generates config.h.in. The setup in this file has to be done by hand. Please note:

./source/CMakeLists.txt

All parts of the library are organized into logical object libraries with their respective sources lying under ./source/<foo>, or ./bundled/<foo>/<...>. The actual setup of an object library happens within that subdirectories with the help of a few macros. More documentation on the unity build subsystem is available here.

#
# A list of source files that, if DEAL_II_UNITY_BUILD=ON, will be concatenated
# into a few unity files:
#
set(_unity_include_src
  block_info.cc
  dof_faces.cc
  ...
  )

#
# A list of source files that are always compiled individually:
#
set(_separate_src
  dof_accessor.cc
  dof_accessor_get.cc
  ...
  )

#
# The number of files, if DEAL_II_UNITY_BUILD=ON, to include in each unity
# file. This number is determined empirically by timing the build. The macro
# SETUP_SOURCE_LIST calls the macro SETUP_UNITY_TARGET which will generate unity
# files that each contain no more than _n_includes_per_unity_file files. If
# DEAL_II_UNITY_BUILD=OFF then this variable is never read.
#
set(_n_includes_per_unity_file 15)

#
# A macro that handles setting up the list of source files to compile in the
# _src variable and handles the unity build logic:
#
setup_source_list("${_unity_include_src}"
  "${_separate_src}"
  ${_n_includes_per_unity_file}
  _src
  )

#
# A list of instantiations that must be expanded:
#
set(_inst
  block_info.inst.in
  ...
  )

#
# The following macro will set up an obj_dofs_debug and
# obj_dofs_release targets with appropriate compile flags and
# definitions.
#
# Header files and instantiation files (${_header}, ${_inst}) are added
# for cosmetic reasons, so that they show up in IDEs.
#
file(GLOB _header
  ${CMAKE_SOURCE_DIR}/include/deal.II/dofs/*.h
  )

deal_ii_add_library(obj_dofs OBJECT ${_src} ${_header} ${_inst})

#
# This macro will set up a target for each of the files listed in
# ${_inst}. Appropriate target dependencies will be added to obj_dofs_debug and
# obj_dofs_release.
#
expand_instantiations(obj_dofs "${_inst}")

Later, all object targets are collected in ./source/CMakeLists.txt to define the actual debug and releases libraries. For further details, see ./source/CMakelists.txt and ./cmake/macros/macro_deal_ii_add_library.cmake.

./cmake/config/CMakeLists.txt

The final bits of configuration happens in ./cmake/config/CMakeLists.txt where the templates for the project configuration deal.IIConfig.cmake gets expanded. Furthermore, the configuration for the template expansion mechanism resides under ./cmake/config/template_arguments.in.


Valid HTML 4.01! Valid CSS!