Porting deal.II
Keywords: deal.II porting

# Porting deal.II to new systems

deal.II uses very few POSIX specific system features and is otherwise fairly ISO (2011) C++ Standard compliant. Consequently, there is a good chance that deal.II will run on a reasonably well behaved system besides the ones listed in the ReadMe. Nevertheless, there are cases where some adjustments are necessary.

## Unknown compiler

Currently, the deal.II CMake build system recognizes gcc, clang, as well as icc, and sets up reasonable default compiler flags.

- To start porting to an unknown compiler, specify all necessary compiler flags by hand via

      DEAL_II_CXX_FLAGS         - used during all builds
      DEAL_II_CXX_FLAGS_DEBUG   - additional flags for the debug library
      DEAL_II_CXX_FLAGS_RELEASE - additional flags for the release library

  After that try to compile the library with minimal external dependencies (`-DDEAL_II_ALLOW_AUTODETECTION=OFF`, for further information see the deal.II CMake documentation).
- For adding permanent support for the unknown compiler to the build system, have a look at

      cmake/setup_compiler_flags.cmake
      cmake/setup_compiler_flags_gnu.cmake
      cmake/setup_compiler_flags_icc.cmake

  Patches are highly welcome! See here for information on how to get in contact with us.
- You might want to have a look at

      cmake/checks/check_01_for_compiler_features.cmake
      cmake/checks/check_01_for_cxx_features.cmake
      cmake/checks/check_03_for_compiler_bugs.cmake
      include/deal.II/base/config.h.in

  to see how compiler specific checks are done.

## Porting to a new platform

deal.II should support almost all reasonably POSIX compliant platforms out of the box. Nevertheless, the following bits of information might help:

- The build system of deal.II uses CMake. So, in order to port deal.II to a new platform, it is obviously necessary that CMake supports the platform in question with at least one generator for a native build tool, see here.
- deal.II is mainly developed with gcc on GNU/Linux, so it is best to begin porting to a new platform with the help of gcc. After that a platform specific compiler might be tried.
- Almost all POSIX specific code is well guarded with fall-back code in case the specific POSIX function is not available. There is (currently) one exception, though: Routines that measure elapsed CPU time in `source/base/timer.cc` have implementations for POSIX and Windows and will not work correctly on other platforms.
- Have a look at

      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
      include/deal.II/base/config.h.in

  to see how platform and compiler specific checks are done.
- Of course, we would be happy to hear about the changes you made for your system, so that we can include them into the next version of the library!

## Cross compiling

It is possible to use the CMake toolchain to cross compile deal.II for a platform other than the one on which the compiler is running. The target platform can have a different operating system, different architecture or different set of libraries. Cross compilation is a very useful technique, for instance it can be used to compile deal.II with a compiler that is not available in the target machine. An alternative technique is to use a Docker container or a virtual machine that mimics the target machine.

You can use any compiler for cross compilation, although LLVM/clang might be more versatile because it supports multiple architecture targets in a single executable natively (see Cross-compilation using Clang). Below you can find an example toolchain file for cross compilation with clang (and another example for Windows64 using MinGW).

    set(target_root /path/to/sysroot)
    set(dealii_dir ${target_root}/path/to/lib/dealii)

    set(CMAKE_SYSTEM_NAME Linux)
    set(CMAKE_SYSTEM_PROCESSOR x86_64)

    set(CMAKE_SYSROOT ${target_root})

    set(CMAKE_C_COMPILER clang)
    set(CMAKE_CXX_COMPILER clang++)

    set(CMAKE_C_COMPILER_EXTERNAL_TOOLCHAIN ${target_root}/path/to/gcc/toolchain)
    set(CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN ${target_root}/path/to/gcc/toolchain)

    set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
    set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
    set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
    set(CMAKE_FIND_ROOT_PATH_MODE_PACKAGE ONLY)

If you use LLVM/clang you can use the gcc toolchain of the target with the option `CMAKE_CXX_COMPILER_EXTERNAL_TOOLCHAIN`, this is equivalent to the clang option --gcc-toolchain. You should place all the relevant libraries of the target in `$target_root`. `ldd` is a great tool figure out the libraries that you need. You can use the C libraries of the target such as Blas, HDF5 and MPI. On the other hand C++ libraries can be problematic because different C++ compilers (or even different versions of the same compiler, or the same compiler on different platforms) mangle public symbols in radically different ways. For this reason C++ libraries such as Trilinos should be cross compiled by the same compiler as deal.II.

If the host and the target have a different architecture, you have to set up a native deal.II build directory first and run `make expand_instantiations_exe` in it. The executable is needed for the build system (the cross compiled version cannot be used if the architecture of the target and the host are not the same). Locate the `expand_instantiations` executable (it usually resides under `${CMAKE_BINARY_DIR}/bin`) and export its location with the `PATH` environment variable. Below you can find a minimal cmake script for the configuration of deal.II.

    mkdir $dealii_build
    cd $dealii_build
    export LD_LIBRARY_PATH=$target_root/lib/directories
    cmake -DCMAKE_TOOLCHAIN_FILE=toolchain.cmake \
            -DDEAL_II_FORCE_BUNDLED_BOOST=ON \
            -DDEAL_II_ALLOW_AUTODETECTION=OFF \
            -DDEAL_II_WITH_MPI=ON \
            -DMPI_CXX_INCLUDE_PATH:STRING=$target_root'/path/to/mpi/include' \
            -DMPI_CXX_LIBRARIES:STRING=$target_root'/path/to/mpi/lib/libmpi_1.so;'$target_root'/path/to/mpi/lib/libmpi_2.so' \
            -DDEAL_II_WITH_TRILINOS=ON \
            -DTRILINOS_DIR=$target_root/path/to/trilinos \
            -DDEAL_II_WITH_P4EST=ON \
            -DP4EST_DIR=$target_root/path/to/p4est \
            -DCMAKE_INSTALL_PREFIX=$target_root/path/to/dealii \
            /path/to/dealii/repository
    make expand_instantiations_exe
    export PATH=$dealii_build/bin/:$PATH
    make -jN install

If the target uses `LD_LIBRARY_PATH` to set up some libraries, you may need to export `LD_PRELOAD_PATH` with those libraries before you call CMake. Note that CMake might not be able to guess the MPI configuration, therefore you may have to give all the MPI flags to CMake. There are two ways to obtain the MPI flags, you can compile another program at the target and then inspect `CMakeCache.txt` or you can obtain the flags using `mpic++ --showme:compile` and `mpic++ --showme:link`. The remaining configuration can be adjusted at will, see the documentation. Note that the rpaths of the examples might not be correct, this can be fixed using `LD_LIBRARY_PATH` or `chrpath`.