Toolchain files and cross-compiling

CMake · Expert · 15 min

Embedded, mobile and multi-architecture builds all rely on toolchain files, and so do vcpkg and Conan.

A toolchain file is a CMake script loaded before the first project() call, passed with --toolchain file.cmake (or -DCMAKE_TOOLCHAIN_FILE=...). It describes the target system, as opposed to the host running the build:

  • CMAKE_SYSTEM_NAME (Linux, Windows, Android, Generic for bare metal) and CMAKE_SYSTEM_PROCESSOR. Setting CMAKE_SYSTEM_NAME is what tells CMake it is cross-compiling (CMAKE_CROSSCOMPILING becomes true).
  • CMAKE_C_COMPILER / CMAKE_CXX_COMPILER, for example aarch64-linux-gnu-g++.
  • CMAKE_SYSROOT: the target's headers and libraries.
  • CMAKE_FIND_ROOT_PATH and the CMAKE_FIND_ROOT_PATH_MODE_* variables: search for libraries, headers and packages only in the target root, but for programs (code generators, tools) on the host.

Because the compiler is fixed at the first configure, switching toolchains requires a fresh build directory.

Things that break when cross-compiling: running freshly built executables during the build (code generators must be built for the host, or found as host tools), try_run checks, and tests (use CMAKE_CROSSCOMPILING_EMULATOR, e.g. qemu-aarch64, to run them).

Package managers use the same hook: vcpkg provides scripts/buildsystems/vcpkg.cmake as a toolchain that installs dependencies and adds them to the search path, and Conan generates conan_toolchain.cmake. A toolchain file can chain to another via include().

Example

# aarch64-linux.cmake  (use: cmake -S . -B build-arm --toolchain aarch64-linux.cmake)
set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)

set(CMAKE_C_COMPILER   aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER aarch64-linux-gnu-g++)
set(CMAKE_SYSROOT /opt/sysroots/aarch64)

# libraries, headers and packages from the target; programs from the host
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)

# run target binaries (tests, gtest discovery) through an emulator
set(CMAKE_CROSSCOMPILING_EMULATOR qemu-aarch64;-L;/opt/sysroots/aarch64)

# vcpkg instead:  cmake -S . -B build --toolchain $VCPKG_ROOT/scripts/buildsystems/vcpkg.cmake

Remember

  • A toolchain file describes the target system and is loaded before project()
  • CMAKE_SYSTEM_NAME turns on cross-compiling; FIND_ROOT_PATH modes separate host and target
  • vcpkg and Conan integrate through toolchain files too

Watch out for

  • Reusing a build directory after changing toolchain files; the compiler is cached at first configure, so start fresh.

Check yourself

  1. Why set CMAKE_FIND_ROOT_PATH_MODE_PROGRAM to NEVER when cross-compiling?

Sign in to answer, see the explanations, and get this lesson back as a quick review later.

Official docs: cmake.org/cmake/help/latest/manual/cmake-toolchains.7.html