Keywords: deal.II

# 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.

## Coding convention

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

- Statements and keywords are written in all caps.
- Indenting is done by two spaces; the usual indenting rules apply.
- The `else()`, `endif()`, `endforeach()`, etc. statements shall not repeat the corresponding condition in `if()`, `foreach()`, etc.
- To emphasize a comment it may be enclosed by a leading and trailing empty comment line.

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()

- Line break is at 78 characters and should be obeyed whenever reasonable.
- Long statements should be broken into several lines at reasonable places. Additional lines for a statement are indented by 2 spaces.
- Multiline statements must end with the closing bracket at a single line:

    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:

- Global (configuration) variables are written in all caps. When introducing a new one, ensure that the name isn't already used somewhere else. Unrelated global variables must never be overwritten.
- Global variables can be prefixed by `DEAL_II_`. (Global variables defined by CMake are usually prefixed by `CMAKE_`.)
- Local variables should always be named in all lowercase with a leading "_". Local variables cannot be assumed to remain valid. The may get overwritten at any time.

## 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:

- `setup_cached_variables.cmake`: This sets up all cached variables prior to the call to `project(deal.II)`. For details see the comment at the top. Furthermore, some bookkeeping for compiler and linker flags takes place, see the section about compile flags.
- `setup_deal_ii.cmake`: This file is included immediately after the call to `project(deal.II)` and will set up all magic numbers such as names, definitions, relative and absolute paths used in the build system. Most of the definitions are guarded with the help of the `SET_IF_EMPTY` macro so that it is possible to override the values from the command line.
- `setup_compiler_flags.cmake` sets up a suitable set of default compile flag for a known compiler by including the appropriate `setup_compiler_flags_*.cmake` file. When adding new flags or compiler support, please respect the following note

      #
      # (./cmake/setup_compiler_flags.cmake)
      #
      # ####################
      # #     FAT NOTE:    #
      # ####################
      #
      # All configuration in setup_compiler_flags.cmake and
      # setup_compiler_flags_.cmake shall ONLY modify:
      #
      #   DEAL_II_CXX_FLAGS
      #   DEAL_II_CXX_FLAGS_DEBUG
      #   DEAL_II_CXX_FLAGS_RELEASE
      #   DEAL_II_LINKER_FLAGS
      #   DEAL_II_LINKER_FLAGS_DEBUG
      #   DEAL_II_LINKER_FLAGS_RELEASE
      #
      # All modifications shall be guarded with the ENABLE_IF_SUPPORTED
      # or ENABLE_IF_LINKS macro, e.g.
      #
      #   enable_if_supported(DEAL_II_CXX_FLAGS "-fpic")
      #   enable_if_links(DEAL_II_LINKER_FLAGS "-Wl,--as-needed")
      #
      # Compiler flags for platform dependent optimization (such as
      # -march=native) must always be guarded with
      # DEAL_II_ALLOW_PLATFORM_INTROSPECTION:
      #
      #   IF(DEAL_II_ALLOW_PLATFORM_INTROSPECTION)
      #     enable_if_supported(DEAL_II_CXX_FLAGS "-march=native")
      #   ENDIF()
      #
      # Checks for compiler features (such as C++17 support) and compiler
      # specific bugs that
      #   - usually set up further configuration (such as preprocessor
      #     definitions)
      #   - disable a specific flag for a specific compiler version.
      #
      # belong the corresponding file:
      #
      #   ./cmake/checks/check_01_cpu_features.cmake
      #   ./cmake/checks/check_01_cxx_features.cmake
      #   ./cmake/checks/check_02_compiler_features.cmake
      #   ./cmake/checks/check_02_system_features.cmake
      #   ./cmake/checks/check_03_compiler_bugs.cmake
      #

### `./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

- A check usually consists of a call to one of the macros below that will set up a global variable. Please stick to the naming convention `HAVE_<..>`, resp. `DEAL_II_(HAVE|USE)_<..>`. It is forbidden to use a variable name starting with `DEAL_II_WITH_<..>` because this prefix is exclusively reserved for the feature mechanism described below. For some tests it might be necessary to manipulate global variables.
- A platform check should have a prominent comment explaining what it does and why it is there, and should state author and year.
- There are a number of readily available platform check macros:

      check_cxx_source_compiles(source variable)
        - Checks whether it is possible to compile _and_ link the code snippet
          <source>. If successful, variable is set to 1.

      check_cxx_source_runs(source variable)
        - variable is set to 1 if <source> could be successfully compiled and
          linked and the resulting program ran and exited without error.
          Avoid this macro outside of a DEAL_II_ALLOW_PLATFORM_INTROSPECTION
          guard. A sensible fallback should be provided if the check cannot
          be run (e.g. when cross compiling).

      check_cxx_compiler_bug(source variable)
        - Inverts the logic of CHECK_CXX_SOURCE_COMPILES(), i.e. variable is
          set to 1 if it was not possible to compile and link <source>.

      check_include_file_cxx(header variable)
        - Check whether it is possible to compile and link a dummy program
          including <header>.

      check_function_exists(function variable)
        - Check for the existence of a function prototype with name
          <function>. (Don't forget to specify the link libraries, see
          below.) Use CHECK_CXX_SYMBOL_EXISTS to search for C++ function
          definitions instead, if possible.

      check_cxx_symbol_exists(symbol header_file variable)
        - Check for the existence of a symbol definition in the header_file
          as well as for the presence in the current link interface
          (Don't forget to specify the link libraries, see below.)

      check_cxx_compiler_flag(flag variable)
        - Sets the variable to 1 if the compiler understands the flag.

- Necessary compiler flags can easily set in the string variable `CMAKE_REQUIRED_FLAGS`. There is a small macro that does this job nicely:

      add_flags(CMAKE_REQUIRED_FLAGS "-Werror")
      check_cxx_source_compiles(...)
      reset_cmake_required()

- Necessary include directories and libraries necessary for linkage can be set in the list variables `CMAKE_REQUIRED_INCLUDES` and `CMAKE_REQUIRED_LIBRARIES`. It is best to append these lists and later on reset `CMAKE_REQUIRED_*` (including `CMAKE_REQUIRED_FLAGS`) to their default values:

      list(APPEND CMAKE_REQUIRED_INCLUDES <a list of includes>)
      list(APPEND CMAKE_REQUIRED_LIBRARIES <a list of libraries>)
      check_cxx_source_compiles(...)
      reset_cmake_required()

### `./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:

- The sole purpose of a find module is to find an external library (no deal.II specific dependency checking, no compatibility checking).
- It should do so by appropriate `deal_ii_find_library`, `deal_ii_find_path` and `deal_ii_find_file` calls (same syntax as the native CMake functions; just a small wrapper to provide some useful output). The results of this calls should be the only cached variables.
- A `WARNING`, `SEND_ERROR` or `FATAL_ERROR` must be avoided (the only exception is the `REQUIRED` keyword).
- The following uncached variables are recognized by the feature configuration mechanism:

      FEATURE_FOUND
      FEATURE_LIBRARIES (with optimized, debug, release keywords)
      FEATURE_LIBRARIES(_DEBUG|_RELEASE)
      FEATURE_INCLUDE_DIRS
      FEATURE_LINKER_FLAGS(|_DEBUG|_RELEASE)
      FEATURE_CXX_FLAGS(|_DEBUG|_RELEASE)
      FEATURE_DEFINITIONS(|_DEBUG|_RELEASE)
      FEATURE_VERSION
      FEATURE_VERSION(_MAJOR|_MINOR|_SUBMINOR)

  The `process_feature` macro should be exclusively used for setting up these variables (except the version variants). An example invocation is

      process_feature(UMFPACK
        LIBRARIES
          REQUIRED UMFPACK_LIBRARY
          OPTIONAL CHOLMOD_LIBRARY CCOLAMD_LIBRARY COLAMD_LIBRARY CAMD_LIBRARY ${_suitesparse_config}
          REQUIRED AMD_LIBRARY
          OPTIONAL METIS_LIBRARIES LAPACK_LIBRARIES rt_LIBRARY
        INCLUDE_DIRS
          REQUIRED UMFPACK_INCLUDE_DIR AMD_INCLUDE_DIR
          OPTIONAL SuiteSparse_config_INCLUDE_DIR
        LINKER_FLAGS
          OPTIONAL LAPACK_LINKER_FLAGS
        CLEAR
          UMFPACK_LIBRARY CHOLMOD_LIBRARY CCOLAMD_LIBRARY COLAMD_LIBRARY
          CAMD_LIBRARY SuiteSparse_config_LIBRARY AMD_LIBRARY UMFPACK_INCLUDE_DIR
          AMD_INCLUDE_DIR SuiteSparse_config_INCLUDE_DIR
        )

  The macro concatenates all specified variables into the final `FEATURE_SUFFIX` variable. Hereby, a feature is successfully found if all `REQUIRED` variables are non-empty and not set to `"-NOTFOUND"`. `OPTIONAL` variables are just filtered out in this case. As a last set of parameters the full list of cached search result variables must be specified after the `CLEAR` keyword - this is used to provide a possibility to undo a feature search.
- Only "local" variables "`_<all lowercase>`" or "global" variables prefixed by `FEATURE_` may be altered. Do not set `DEAL_II_*` or `CMAKE_*` variables directly!
- A hint with `FEATURE_DIR` can be set up for convenience. It is best to start the `Find` module by

      set(FEATURE_DIR "" CACHE PATH "short description")
      set_if_empty(FEATURE_DIR "$ENV{FEATURE_DIR}")

  and use `FEATURE_DIR` as a hint.

### `./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):

- `<FEATURE>` means all caps, `<feature>` means all lowercase
- In `./cmake/configure/configure_<feature>.cmake`:

      FEATURE_<FEATURE>_DEPENDS              (a variable)
        - a variable which contains an optional list of other features
          this feature depends on (and which have to be enabled for this feature
          to work.)
          Features must be given with short name, i.e. without DEAL_II_WITH_

      FEATURE_<FEATURE>_AFTER                (a variable)
        - a variable which contains an optional list of other features
          that have to be configured prior to this feature
          Features must be given with short name, i.e. without DEAL_II_WITH_

      FEATURE_<FEATURE>_FIND_EXTERNAL(var)   (a macro)
        - which should set var to TRUE if all dependencies for the feature are
          fulfilled. In this case all necessary variables for
          FEATURE_<FEATURE>_CONFIGURE_EXTERNAL must be set.
          Otherwise var should remain unset.
          If this macro is undefined, FIND_PACKAGE(<FEATURES>) is
          called directly instead.

      FEATURE_<FEATURE>_CONFIGURE_EXTERNAL()  (a macro)
        - which should setup all necessary configuration for the feature with
          external dependencies. If something goes wrong this macro must
          issue a FATAL_ERROR.
          If this macro is undefined, the information provided in
          <FEATURES>_LIBRARIES, <FEATURES>_INCLUDE_DIRS and
          <FEATURES>_LINKER_FLAGS is used for the build.

      FEATURE_<FEATURE>_CONFIGURE_BUNDLED()  (a macro)
        - which should setup all necessary configuration for the feature with
          bundled source dependencies. If something goes wrong this macro must
          issue a FATAL_ERROR.

      FEATURE_<FEATURE>_ERROR_MESSAGE()      (macro)
        - which should print a meaningful error message (with FATAL_ERROR) for
          the case that no external library was found (and bundled is not
          allowed to be used.) If not defined, a suitable default error message
          will be printed.

- In `./bundled/configure_bundled.cmake`:

      FEATURE_<FEATURE>_HAVE_BUNDLED         (a boolean)
        - which should either be set to TRUE if all necessary libraries of the
          features comes bundled with deal.II and hence can be supported
          without external dependencies, or unset.

      DEAL_II_FORCE_BUNDLED_<FEATURE>        (an option)
        - If <feature> can be set up by bundled libraries, this
          configuration option must be present to force a use of bundled
          dependencies

- Furthermore, if `DEAL_II_FEATURE_<FEATURE>_BUNDLED_CONFIGURED` is set to `TRUE` the file `./bundled/CMakeLists.txt` must compile and install the bundled package appropriately.

### 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 general (internal) logic for variables applies:
  - A variable name without `_DEBUG` or `_RELEASE`: Used for all targets
  - `<...>_DEBUG`: additionally used for debug targets
  - `<...>_RELEASE`: additionally used for release targets
- For internal and external use, used to keep track of external libraries, the deal.II library and user programs have to be linked against:
  - `DEAL_II_LIBRARIES`
  - `DEAL_II_LIBRARIES_DEBUG`
  - `DEAL_II_LIBRARIES_RELEASE`
- Used to keep track of external include dirs, necessary for the compilation of the library and user programs: deal.II library:
  - `DEAL_II_INCLUDE_DIRS`
- Include dirs from bundled packages necessary for the compilation of the library and user projects out of the build directory:
  - `DEAL_II_BUNDLED_INCLUDE_DIRS`
- For setting necessary preprocessor definitions (`-D<...>`) for the compilation of the deal.II library:
  - `DEAL_II_DEFINITIONS`
  - `DEAL_II_DEFINITIONS_DEBUG`
  - `DEAL_II_DEFINITIONS_RELEASE`
- For internal and external use, for setting necessary compiler flags, e.g. `-std=c++20` (if available):
  - `DEAL_II_CXX_FLAGS`
  - `DEAL_II_CXX_FLAGS_DEBUG`
  - `DEAL_II_CXX_FLAGS_RELEASE`
- For internal and external use, for setting necessary linker flags for the deal.II library and user programs:
  - `DEAL_II_LINKER_FLAGS`
  - `DEAL_II_LINKER_FLAGS_DEBUG`
  - `DEAL_II_LINKER_FLAGS_RELEASE`

### 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:

- `config.h.in` should only contain a minimum of necessary compile definitions to avoid unnecessary recompilation if configuration changes.
- Definition toggles in `config.h.in` should have a prominent comment explaining it and should be grouped by file exporting the definition.

### `./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`.