CMAKE-TOOLCHAINS(7)

πŸŽ€ CMAKE-TOOLCHAINS πŸŽ€

ok so this is the cmake-toolchains page from the CMake manual β€” section 7, bestie β€” aka the CMake Toolchains Reference ✨ it's literally the entire lore of how CMake picks its compilers and utilities, so buckle up 🫢


πŸ’– INTRODUCTION πŸ’–

So here's the vibe: CMake uses a whole toolchain of utilities to compile stuff, link libraries, create archives, and do all the other little tasks that drive the build. Which toolchain utilities are actually available? That depends on which languages you've enabled, periodt.

In normal builds, CMake just auto-detects the toolchain for host builds based on system introspection and defaults β€” it's giving main character who knows what she's doing πŸ’… In cross-compiling scenarios though, you can specify a toolchain file with all the info about compiler and utility paths.

Added in version 3.19: you can use cmake-presets(7) to specify toolchain files. iconic ✨

🌸 LANGUAGES 🌸

Languages get enabled by the project() command. Language-specific built-in variables β€” things like CMAKE_CXX_COMPILER, CMAKE_CXX_COMPILER_ID, etc. β€” get set by invoking project(). And if there's no project command in the top-level CMakeLists file, one gets implicitly generated for you. By default the enabled languages are C and CXX:

project(C_Only C)

There's also a special value NONE you can pass to project() to enable no languages at all:

project(MyProject NONE)

And the enable_language() command lets you turn languages on after the project() command:

enable_language(CXX)

When a language is enabled, CMake finds a compiler for that language and figures out a bunch of info: the vendor and version of the compiler, the target architecture and bitwidth, the location of the corresponding utilities, etc. detective mode fr πŸ”

The ENABLED_LANGUAGES global property holds all the languages that are currently enabled πŸ‘‘


πŸ¦‹ VARIABLES AND PROPERTIES πŸ¦‹

Several variables relate to the language components of a toolchain that are enabled β€” here's the roster:

  • CMAKE_<LANG>_COMPILER β€” the full path to the compiler used for <LANG>.
  • CMAKE_<LANG>_COMPILER_ID β€” the compiler identifier that CMake uses.
  • CMAKE_<LANG>_COMPILER_VERSION β€” the version of the compiler.
  • CMAKE_<LANG>_FLAGS β€” these variables and the configuration-specific equivalents contain flags that get added to all invocations of the compiler for a particular language, including the ones driving compiling and linking.
  • CMAKE_<LANG>_LINK_FLAGS β€” added in version 4.3 ✨ these variables and the configuration-specific equivalents contain flags that get added to all invocations of the compiler for a particular language when driving linking only.

Now, CMake needs a way to figure out which compiler to use to invoke the linker. That's determined by the LANGUAGE property of source files of the target, and in the case of static libraries, by the LANGUAGE of the dependent libraries. And if you don't like the choice CMake makes? You can override it with the LINKER_LANGUAGE target property 😌


πŸ”₯ TOOLCHAIN FEATURES πŸ”₯

CMake gives you the try_compile() command plus modules like CheckSourceCompiles, CheckCXXSymbolExists and CheckIncludeFile to test the capability and availability of various toolchain features. These APIs test the toolchain in some way and then cache the result so the test doesn't have to happen all over again the next time CMake runs β€” efficiency slay πŸ’…

Some toolchain features have built-in handling in CMake and don't need compile-tests at all. For example, POSITION_INDEPENDENT_CODE lets you specify that a target should be built as position-independent code, if the compiler supports that feature. And the <LANG>_VISIBILITY_PRESET and VISIBILITY_INLINES_HIDDEN target properties add flags for hidden visibility, again if the compiler supports it 🫢


🌈 CROSS COMPILING 🌈

If cmake(1) is invoked with the command line parameter --toolchain path/to/file or -DCMAKE_TOOLCHAIN_FILE=path/to/file, that file gets loaded early to set values for the compilers. And the CMAKE_CROSSCOMPILING variable is set to true whenever CMake is cross-compiling πŸ’€

⚠️ ngl, note that using the CMAKE_SOURCE_DIR or CMAKE_BINARY_DIR variables inside a toolchain file is typically undesirable. The toolchain file gets used in contexts where these variables have different values depending on where they're used (e.g. as part of a call to try_compile()). In most cases, when you need to evaluate paths inside a toolchain file, the way more appropriate variable is CMAKE_CURRENT_LIST_DIR, since it always has an unambiguous, predictable value. trust 🫑

πŸ“ Cross Compiling for Linux πŸ“

A typical cross-compiling toolchain for Linux has content like this:

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(CMAKE_SYSROOT /home/devel/rasp-pi-rootfs)
set(CMAKE_STAGING_PREFIX /home/devel/stage)

set(tools /home/devel/gcc-4.7-linaro-rpi-gnueabihf)
set(CMAKE_C_COMPILER ${tools}/bin/arm-linux-gnueabihf-gcc)
set(CMAKE_CXX_COMPILER ${tools}/bin/arm-linux-gnueabihf-g++)

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)

Where:

  • CMAKE_SYSTEM_NAME β€” is the CMake-identifier of the target platform to build for.
  • CMAKE_SYSTEM_PROCESSOR β€” is the CMake-identifier of the target architecture.
  • CMAKE_SYSROOT β€” is optional, and may be specified if a sysroot is available.
  • CMAKE_STAGING_PREFIX β€” is also optional. It may be used to specify a path on the host to install to. The CMAKE_INSTALL_PREFIX is always the runtime installation location, even when cross-compiling.
  • CMAKE_<LANG>_COMPILER β€” this variable may be set to full paths, or to names of compilers to search for in standard locations. For toolchains that don't support linking binaries without custom flags or scripts, you may set the CMAKE_TRY_COMPILE_TARGET_TYPE variable to STATIC_LIBRARY to tell CMake not to try to link executables during its checks πŸ‘€

CMake find_* commands will look in the sysroot and the CMAKE_FIND_ROOT_PATH entries by default in all cases, and will also look in the host system root prefix. This can be controlled on a case-by-case basis, but when cross-compiling it can be useful to exclude looking in either the host or the target for particular artifacts. Generally, includes, libraries and packages should be found in the target system prefixes, whereas executables which must be run as part of the build should be found only on the host and not on the target. That's literally the whole purpose of the CMAKE_FIND_ROOT_PATH_MODE_* variables πŸ’–

🧚 Cross Compiling for the Cray Linux Environment 🧚

Cross compiling for compute nodes in the Cray Linux Environment can be done without needing a separate toolchain file. Specifying -DCMAKE_SYSTEM_NAME=CrayLinuxEnvironment on the CMake command line will ensure that the appropriate build settings and search paths are configured. The platform will pull its configuration from the current environment variables, and will configure a project to use the compiler wrappers from the Cray Programming Environment's PrgEnv-* modules if they're present and loaded ✨

The default configuration of the Cray Programming Environment is to only support static libraries. This can be overridden and shared libraries enabled by setting the CRAYPE_LINK_TYPE environment variable to dynamic.

Running CMake without specifying CMAKE_SYSTEM_NAME will run the configure step in host mode, assuming a standard Linux environment. And if it's not overridden, the PrgEnv-* compiler wrappers will end up getting used, which β€” if you're targeting either the login node or the compute node β€” is likely not the desired behavior πŸ’€ The exception to this would be if you're building directly on a NID instead of cross-compiling from a login node. If you're trying to build software for a login node, you'll need to either first unload the currently loaded PrgEnv-* module, or explicitly tell CMake to use the system compilers in /usr/bin instead of the Cray wrappers. If instead targeting a compute node is what you want, just specify the CMAKE_SYSTEM_NAME as mentioned above πŸ‘‘

πŸ’œ Cross Compiling using Clang πŸ’œ

Some compilers, like Clang, are inherently cross compilers. The CMAKE_<LANG>_COMPILER_TARGET can be set to pass a value to those supported compilers when compiling:

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(triple arm-linux-gnueabihf)

set(CMAKE_C_COMPILER clang)
set(CMAKE_C_COMPILER_TARGET ${triple})
set(CMAKE_CXX_COMPILER clang++)
set(CMAKE_CXX_COMPILER_TARGET ${triple})

Similarly, some compilers don't ship their own supplementary utilities like linkers, but they do provide a way to specify the location of the external toolchain that'll be used by the compiler driver. The CMAKE_<LANG>_COMPILER_EXTERNAL_TOOLCHAIN variable can be set in a toolchain file to pass the path to the compiler driver 🫢

⭐ Cross Compiling for QNX ⭐

Just like the Clang compiler, the QNX QCC compiler is inherently a cross compiler. And the CMAKE_<LANG>_COMPILER_TARGET can be set to pass a value to those supported compilers when compiling:

set(CMAKE_SYSTEM_NAME QNX)

set(arch gcc_ntoaarch64)

set(CMAKE_C_COMPILER qcc)
set(CMAKE_C_COMPILER_TARGET ${arch})
set(CMAKE_CXX_COMPILER q++)
set(CMAKE_CXX_COMPILER_TARGET ${arch})

set(CMAKE_SYSROOT $ENV{QNX_TARGET})

πŸŽ€ Cross Compiling for Windows CE πŸŽ€

Cross compiling for Windows CE requires the corresponding SDK being installed on your system. These SDKs are usually installed under C:/Program Files (x86)/Windows CE Tools/SDKs πŸ“


A toolchain file to configure Visual Studio Generators for Windows CE? Lowkey iconic, here's what it looks like:

set(CMAKE_SYSTEM_NAME WindowsCE)

set(CMAKE_SYSTEM_VERSION 8.0)
set(CMAKE_SYSTEM_PROCESSOR arm)

set(CMAKE_GENERATOR_TOOLSET CE800) # Can be omitted for 8.0
set(CMAKE_GENERATOR_PLATFORM SDK_AM335X_SK_WEC2013_V310)

So CMAKE_GENERATOR_PLATFORM is the one telling the generator which SDK to use, bestie. And then CMAKE_SYSTEM_VERSION tells the generator what version of Windows CE to use. Right now version 8.0 (that's Windows Embedded Compact 2013) is supported out of the box β€” like, it just works. Other versions? You might have to set CMAKE_GENERATOR_TOOLSET to the correct value yourself. πŸ™ˆ

πŸ’» Cross Compiling for Windows 10 Universal Applications

A toolchain file to configure Visual Studio Generators for a Windows 10 Universal Application may look like this:

set(CMAKE_SYSTEM_NAME WindowsStore)
set(CMAKE_SYSTEM_VERSION 10.0)

A Windows 10 Universal Application targets both Windows Store and Windows Phone β€” dual main character energy fr fr. So you specify CMAKE_SYSTEM_VERSION to be 10.0 or higher.

CMake selects a Windows SDK as described by documentation of the CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION variable. (iykyk)

πŸ“± Cross Compiling for Windows Phone

A toolchain file to configure Visual Studio Generators for Windows Phone may look like this:

set(CMAKE_SYSTEM_NAME WindowsPhone)
set(CMAKE_SYSTEM_VERSION 8.1)

πŸ›οΈ Cross Compiling for Windows Store

A toolchain file to configure a Visual Studio Generators for Windows Store may look like this:

set(CMAKE_SYSTEM_NAME WindowsStore)
set(CMAKE_SYSTEM_VERSION 8.1)

πŸŽ›οΈ Cross Compiling for ADSP SHARC/Blackfin

Okay so cross-compiling for ADSP SHARC or Blackfin gets configured by setting CMAKE_SYSTEM_NAME to ADSP and CMAKE_SYSTEM_PROCESSOR to the "part number", minus the ADSP- prefix β€” so like 21594, SC589, etc. And no cap, that value is case insensitive. πŸ’…

CMake will automatically search for CCES or VDSP++ installs in their default install locations and select the most recent version found. And if you've got both installed? CCES wins, always, it gets selected over VDSP++. Custom install paths can be set via the CMAKE_ADSP_ROOT variable or the ADSP_ROOT environment variable.

The compiler (cc21k vs. ccblkfn) is selected automatically based on the CMAKE_SYSTEM_PROCESSOR value you provided. The algorithm ate, honestly.

πŸ€– Cross Compiling for Android

A toolchain file may configure cross-compiling for Android by setting CMAKE_SYSTEM_NAME to Android. Everything past that is specific to whichever Android development environment you're using.

For Visual Studio Generators, CMake expects NVIDIA Nsight Tegra Visual Studio Edition or the Visual Studio tools for Android to be installed. Go check those sections for the further configuration deets.

For Makefile Generators and the Ninja generator, CMake expects one of these environments:

  • NDK
  • Standalone Toolchain

CMake uses the following steps to pick one:

  • If the CMAKE_ANDROID_NDK variable is set, the NDK at the specified location will be used.
  • Else, if the CMAKE_ANDROID_STANDALONE_TOOLCHAIN variable is set, the Standalone Toolchain at the specified location will be used.
  • Else, if the CMAKE_SYSROOT variable is set to a directory of the form <ndk>/platforms/android-<api>/arch-<arch>, the <ndk> part will be used as the value of CMAKE_ANDROID_NDK and the NDK will be used.
  • Else, if the CMAKE_SYSROOT variable is set to a directory of the form <standalone-toolchain>/sysroot, the <standalone-toolchain> part will be used as the value of CMAKE_ANDROID_STANDALONE_TOOLCHAIN and the Standalone Toolchain will be used.
  • Else, if a cmake variable ANDROID_NDK is set it will be used as the value of CMAKE_ANDROID_NDK, and the NDK will be used.
  • Else, if a cmake variable ANDROID_STANDALONE_TOOLCHAIN is set, it will be used as the value of CMAKE_ANDROID_STANDALONE_TOOLCHAIN, and the Standalone Toolchain will be used.
  • Else, if an environment variable ANDROID_NDK_ROOT or ANDROID_NDK is set, it will be used as the value of CMAKE_ANDROID_NDK, and the NDK will be used.
  • Else, if an environment variable ANDROID_STANDALONE_TOOLCHAIN is set then it will be used as the value of CMAKE_ANDROID_STANDALONE_TOOLCHAIN, and the Standalone Toolchain will be used.
  • Else, an error diagnostic will be issued that neither the NDK or Standalone Toolchain can be found. 😭 (the pipeline said no)

Added in version 3.20: If an Android NDK is selected, its version number is reported in the CMAKE_ANDROID_NDK_VERSION variable. Slay.

πŸ“² Cross Compiling for Android with the NDK

A toolchain file may configure Makefile Generators, Ninja Generators, or Visual Studio Generators to target Android for cross-compiling.

Configure use of an Android NDK with the following variables:

  • CMAKE_SYSTEM_NAME β€” Set to Android. Must be specified to enable cross compiling for Android.
  • CMAKE_SYSTEM_VERSION β€” Set to the Android API level. If not specified, the value is determined as follows:
    • If the CMAKE_ANDROID_API variable is set, its value is used as the API level.
    • If the CMAKE_SYSROOT variable is set, the API level is detected from the NDK directory structure containing the sysroot.
    • Otherwise, the latest API level available in the NDK is used.
  • CMAKE_ANDROID_ARCH_ABI β€” Set to the Android ABI (architecture). If not specified, this variable will default to the first supported ABI in the list of armeabi, armeabi-v7a and arm64-v8a. The CMAKE_ANDROID_ARCH variable will be computed from CMAKE_ANDROID_ARCH_ABI automatically. Also see the CMAKE_ANDROID_ARM_MODE and CMAKE_ANDROID_ARM_NEON variables.
  • CMAKE_ANDROID_NDK β€” Set to the absolute path to the Android NDK root directory. If not specified, a default for this variable will be chosen as specified above.
  • CMAKE_ANDROID_NDK_DEPRECATED_HEADERS β€” Set to a true value to use the deprecated per-api-level headers instead of the unified headers. If not specified, the default will be false unless using a NDK that does not provide unified headers.
  • CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION β€” On NDK r19 or above, this variable must be unset or set to clang. On NDK r18 or below, set this to the version of the NDK toolchain to be selected as the compiler. If not specified, the default will be the latest available GCC toolchain.
  • CMAKE_ANDROID_STL_TYPE β€” Set to specify which C++ standard library to use. If not specified, a default will be selected as described in the variable documentation.

The following variables will be computed and provided automatically β€” you don't gotta lift a finger 🫢:

  • CMAKE_<LANG>_ANDROID_TOOLCHAIN_PREFIX β€” The absolute path prefix to the binutils in the NDK toolchain.
  • CMAKE_<LANG>_ANDROID_TOOLCHAIN_SUFFIX β€” The host platform suffix of the binutils in the NDK toolchain.

For example, a toolchain file might contain:

set(CMAKE_SYSTEM_NAME Android)
set(CMAKE_SYSTEM_VERSION 21) # API level
set(CMAKE_ANDROID_ARCH_ABI arm64-v8a)
set(CMAKE_ANDROID_NDK /path/to/android-ndk)
set(CMAKE_ANDROID_STL_TYPE gnustl_static)

Alternatively one may specify the values without a toolchain file at all:

$ cmake ../src \
  -DCMAKE_SYSTEM_NAME=Android \
  -DCMAKE_SYSTEM_VERSION=21 \
  -DCMAKE_ANDROID_ARCH_ABI=arm64-v8a \
  -DCMAKE_ANDROID_NDK=/path/to/android-ndk \
  -DCMAKE_ANDROID_STL_TYPE=gnustl_static

🧰 Cross Compiling for Android with a Standalone Toolchain

A toolchain file may configure Makefile Generators or the Ninja generator to target Android for cross-compiling using a standalone toolchain.

Configure use of an Android standalone toolchain with the following variables:

  • CMAKE_SYSTEM_NAME β€” Set to Android. Must be specified to enable cross compiling for Android.
  • CMAKE_ANDROID_STANDALONE_TOOLCHAIN β€” Set to the absolute path to the standalone toolchain root directory. A ${CMAKE_ANDROID_STANDALONE_TOOLCHAIN}/sysroot directory must exist. If not specified, a default for this variable will be chosen as specified above.
  • CMAKE_ANDROID_ARM_MODE β€” When the standalone toolchain targets ARM, optionally set this to ON to target 32-bit ARM instead of 16-bit Thumb. See variable documentation for details.
  • CMAKE_ANDROID_ARM_NEON β€” When the standalone toolchain targets ARM v7, optionally set this to ON to target ARM NEON devices. See variable documentation for details.

The following variables will be computed and provided automatically ✨:

  • CMAKE_SYSTEM_VERSION β€” The Android API level detected from the standalone toolchain.
  • CMAKE_ANDROID_ARCH_ABI β€” The Android ABI detected from the standalone toolchain.
  • CMAKE_<LANG>_ANDROID_TOOLCHAIN_PREFIX β€” the absolute path prefix pointing at the binutils inside the standalone toolchain πŸ’«
  • CMAKE_<LANG>_ANDROID_TOOLCHAIN_SUFFIX β€” the host platform suffix of those same binutils in the standalone toolchain πŸŽ€

So like, a toolchain file could literally just contain this and it slays:

set(CMAKE_SYSTEM_NAME Android)
set(CMAKE_ANDROID_STANDALONE_TOOLCHAIN /path/to/android-toolchain)

Or bestie, you can skip the toolchain file entirely and pass the values straight on the command line, no cap:

$ cmake ../src \
  -DCMAKE_SYSTEM_NAME=Android \
  -DCMAKE_ANDROID_STANDALONE_TOOLCHAIN=/path/to/android-toolchain

🌈 Cross Compiling for Android with NVIDIA Nsight Tegra Visual Studio Edition

Okay so if you wanna point one of the Visual Studio Generators at Android using NVIDIA Nsight Tegra, your toolchain file can be this iconic little one-liner:

set(CMAKE_SYSTEM_NAME Android)

And CMAKE_GENERATOR_TOOLSET is the variable you set to pick the Nsight Tegra "Toolchain Version" value πŸ‘‘

Also, the target properties over here are lowkey essential, go look at them:

  • ANDROID_ANT_ADDITIONAL_OPTIONS
  • ANDROID_API_MIN
  • ANDROID_API
  • ANDROID_ARCH
  • ANDROID_ASSETS_DIRECTORIES
  • ANDROID_GUI
  • ANDROID_JAR_DEPENDENCIES
  • ANDROID_JAR_DIRECTORIES
  • ANDROID_JAVA_SOURCE_DIR
  • ANDROID_NATIVE_LIB_DEPENDENCIES
  • ANDROID_NATIVE_LIB_DIRECTORIES
  • ANDROID_PROCESS_MAX
  • ANDROID_PROGUARD_CONFIG_PATH
  • ANDROID_PROGUARD
  • ANDROID_SECURE_PROPS_PATH
  • ANDROID_SKIP_ANT_STEP
  • ANDROID_STL_TYPE

🍎 Cross Compiling for iOS, tvOS, visionOS, or watchOS

So if you're cross-compiling to iOS, tvOS, visionOS, or watchOS, the recommended generator is Xcode, periodt. The Unix Makefiles and Ninja generators do work too β€” but like, they make your project handle way more stuff itself, things like target CPU selection and code signing 😭. More main-character responsibility, ykwim.

You can target any of the Apple device platforms by setting CMAKE_SYSTEM_NAME to one of the values in the lil table below. By default the latest Device SDK gets chosen ✨. And as with all Apple platforms, you can pick a different SDK (a simulator one, say) by setting CMAKE_OSX_SYSROOT β€” though ngl you'll rarely need to (peep Switching Between Device and Simulator down below πŸ‘€). Wanna see which SDKs you actually have? Just run xcodebuild -showsdks.

  • iOS β€” CMAKE_SYSTEM_NAME is iOS; Device SDK (default) iphoneos; Simulator SDK iphonesimulator; Catalyst SDK macosx 🌸
  • tvOS β€” CMAKE_SYSTEM_NAME is tvOS; Device SDK (default) appletvos; Simulator SDK appletvsimulator; Catalyst SDK N/A πŸ¦‹
  • visionOS β€” CMAKE_SYSTEM_NAME is visionOS; Device SDK (default) xros; Simulator SDK xrsimulator; Catalyst SDK N/A πŸ’œ
  • watchOS β€” CMAKE_SYSTEM_NAME is watchOS; Device SDK (default) watchos; Simulator SDK watchsimulator; Catalyst SDK N/A ⌚

So for example, to make a CMake configuration for iOS, this command is literally all you need, bestie:

cmake .. -GXcode -DCMAKE_SYSTEM_NAME=iOS

The variable CMAKE_OSX_ARCHITECTURES sets architectures for both device and simulator. And CMAKE_OSX_DEPLOYMENT_TARGET sets your iOS/tvOS/visionOS/watchOS deployment target 🎯

Next example installs five architectures into a universal binary for an iOS library πŸ”₯. It adds the relevant -miphoneos-version-min=9.3 or -mios-simulator-version-min=9.3 compiler flag wherever it fits. But heads up bestie: the CMAKE_IOS_INSTALL_COMBINED variable used in that example is deprecated now, so this whole approach is no longer recommended πŸ’€.

$ cmake -S. -B_builds -GXcode \
    -DCMAKE_SYSTEM_NAME=iOS \
    "-DCMAKE_OSX_ARCHITECTURES=armv7;armv7s;arm64;i386;x86_64" \
    -DCMAKE_OSX_DEPLOYMENT_TARGET=9.3 \
    -DCMAKE_INSTALL_PREFIX=`pwd`/_install \
    -DCMAKE_XCODE_ATTRIBUTE_ONLY_ACTIVE_ARCH=NO \
    -DCMAKE_IOS_INSTALL_COMBINED=YES

Example:

# CMakeLists.txt
cmake_minimum_required(VERSION 3.14)
project(foo)
add_library(foo foo.cpp)
install(TARGETS foo DESTINATION lib)

Install:

$ cmake --build _builds --config Release --target install

Check the library:

$ lipo -info _install/lib/libfoo.a
Architectures in the fat file: _install/lib/libfoo.a are: i386 armv7 armv7s x86_64 arm64

$ otool -l _install/lib/libfoo.a | grep -A2 LC_VERSION_MIN_IPHONEOS
      cmd LC_VERSION_MIN_IPHONEOS
  cmdsize 16
  version 9.3

πŸ” Code Signing

Some build artifacts for the embedded Apple platforms require code signing, like it's not optional, it's mandatory fr fr. If you're using the Xcode generator and signing is required or you just want it, you can set your development team ID with the CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM CMake variable 🫢. That team ID then gets baked into the generated Xcode project. And by default, CMake avoids needing code signing at all during the internal configuration phase (aka compiler ID and feature detection) β€” cute of her honestly ✨

πŸ”„ Switching Between Device and Simulator

When you configure for any of the embedded platforms, you target either real devices or the simulator. They each have their own separate SDK β€” but CMake only supports specifying a single SDK for the configuration phase 😭. So you, the developer, have to pick one or the other at configure time, no way around it.

With the Xcode generator this is way less of a limitation, because Xcode still lets you build for either a device or a simulator even though configuration only happened for one of the two πŸ‘‘. Inside the Xcode IDE, builds run for whichever "destination" platform is selected. And from the command line, you can name the sdk you want by passing a -sdk option straight to the underlying build tool (xcodebuild). Like so:

$ cmake --build ... -- -sdk iphonesimulator
Bestie, please note πŸ™ˆ: the checks made during configuration were run against the configure-time SDK, so they might not hold true for other SDKs. Commands like find_package(), find_library(), etc. store and use details only for the configured SDK/platform β€” which makes them problematic if you wanna switch between device and simulator builds. To make a device + simulator configuration actually work, follow these rules:
  • Use the explicit -l linker flag, e.g. target_link_libraries(foo PUBLIC "-lz")
  • Use the explicit -framework linker flag, e.g. target_link_libraries(foo PUBLIC "-framework CoreFoundation")
  • Use find_package() only for libraries installed with the CMAKE_IOS_INSTALL_COMBINED feature

🧚 Cross Compiling for Emscripten

Added in version 4.2.

A toolchain file can set up cross-compiling for Emscripten by setting the CMAKE_SYSTEM_NAME variable to Emscripten. Here's what a toolchain file might contain, bestie:

set(CMAKE_SYSTEM_NAME Emscripten)
set(CMAKE_C_COMPILER /path/to/emcc)
set(CMAKE_CXX_COMPILER /path/to/em++)

🌸 Cross Compiling using Renesas compilers

Cross-compiling with Renesas compilers? You've gotta specify at least these:

  • CMAKE_SYSTEM_NAME β€” set it to Generic. This one must be specified to enable cross compiling, no skipping πŸ’…
  • CMAKE_C_COMPILER (CMAKE_<LANG>_COMPILER) β€” set it to the path of the Renesas C compiler, e.g. ccrx, ccrl, or ccrh
  • CMAKE_C_FLAGS (CMAKE_<LANG>_FLAGS) β€” set it to the -isa= or -cpu= flag the compiler requires

Peep the example toolchain files in the sections right below πŸ‘‡

🧑 Renesas CC-RX

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_C_COMPILER "ccrx.exe")
set(CMAKE_ASM_COMPILER "ccrx.exe") # if using ASM language
set(CMAKE_C_FLAGS "-isa=rxv3") # specify the version of target RX CPU
set(CMAKE_EXE_LINKER_FLAGS "-lnkopt=<your linker option here>")

πŸ’› Renesas CC-RL

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_C_COMPILER "ccrl.exe")
set(CMAKE_ASM_COMPILER "ccrl.exe") # if using ASM language
set(CMAKE_C_FLAGS "-cpu=S3") # specify the version of target RL CPU
# To avoid test executable runs out of const section's size.
set(CMAKE_TRY_COMPILE_TARGET_TYPE STATIC_LIBRARY)
# Specifying device file and section layout linker options through compiler driver.
set(CMAKE_EXE_LINKER_FLAGS "-lnkopt=-device=dr5f10y14.dvf -lnkopt=-auto_section_layout")

Renesas CC-RH πŸ’œ

okay so Renesas CC-RH is a whole vibe and honestly the setup is kinda minimal, which we love. here's the cross-compiling toolchain file, bestie, and yes it's literally this short:

set(CMAKE_SYSTEM_NAME Generic)
set(CMAKE_C_COMPILER "ccrh.exe")
set(CMAKE_ASM_COMPILER "ccrh.exe") # if using ASM language
set(CMAKE_C_FLAGS "-Xcommon=rh850") # specify the version of target RH850 CPU
set(CMAKE_EXE_LINKER_FLAGS "-lnkopt=<your linker option here>")

some notes so you don't get caught out 🫢

  • set(CMAKE_SYSTEM_NAME Generic) β€” the system name is literally Generic, and that's what tells CMake "we're cross compiling, act accordingly."
  • set(CMAKE_C_COMPILER "ccrh.exe") β€” the C compiler is ccrh.exe. note the .exe, she's a Windows-y girlie.
  • set(CMAKE_ASM_COMPILER "ccrh.exe") β€” same binary handles ASM, but only if you're using the ASM language (# if using ASM language, the comment says ftw).
  • set(CMAKE_C_FLAGS "-Xcommon=rh850") β€” this is where you specify the version of target RH850 CPU, so -Xcommon=rh850 in this example. swap it for your actual RH850 flavour, iykyk.
  • set(CMAKE_EXE_LINKER_FLAGS "-lnkopt=<your linker option here>") β€” the linker flags go through -lnkopt=... and you fill in your linker option. not me leaving a placeholder, it's literally in the docs πŸ’…

and that's it β€” no CMAKE_FIND_ROOT_PATH gymnastics required here, this one ate and left no crumbs. ✨


2000-2026 Kitware, Inc. and Contributors


and that's the whole page, bestie!! πŸŽ€ you made it to the end, so here's the cute sign-off: this is CMAKE-TOOLCHAINS(7), version 4.4.5, dated Oct 28, 2026, brought to you by the Kitware girlies and their contributors, who have been holding it down since 2000 πŸ’– go forth and cross-compile, you absolute icon πŸ‘‘βœ¨

πŸ“¦ where this page comes from