cmake-toolchains(7)

简介

CMake 使用一系列工具来编译、链接库、创建归档文件以及执行其他构建任务。可用的工具集取决于启用的语言。在常规构建中,CMake 会根据系统内省和默认设置自动确定主机构建的工具链。在交叉编译场景中,可以使用 `--toolchain` 文件指定有关编译器和工具路径的信息。

Added in version 3.19: 可以使用 cmake-presets(7) 来指定工具链文件。

语言

project() 命令用于启用语言。调用 project() 命令时会设置特定语言的内置变量,例如 CMAKE_CXX_COMPILER, CMAKE_CXX_COMPILER_ID 等。如果顶层 CMakeLists 文件中没有 project() 命令,则会隐式生成一个。默认启用的语言是 CCXX

project(C_Only C)

可以使用 NONE 作为 project() 命令的特殊值来启用任何语言。

project(MyProject NONE)

可以在 project() 命令之后使用 enable_language() 命令来启用语言。

enable_language(CXX)

当启用一种语言时,CMake 会查找该语言的编译器,并确定一些信息,例如编译器的供应商和版本、目标体系结构和位数、相关工具的位置等。

全局属性 ENABLED_LANGUAGES 包含当前启用的语言。

变量和属性

有几个变量与启用的工具链的语言组件相关。

CMAKE_<LANG>_COMPILER

用于 <LANG> 的编译器的完整路径。

CMAKE_<LANG>_COMPILER_ID

CMake 使用的编译器标识符。

CMAKE_<LANG>_COMPILER_VERSION

编译器的版本。

CMAKE_<LANG>_FLAGS

该变量及其特定于配置的等效变量包含在编译特定语言文件时将添加到编译命令的标志。

CMake 需要一种方法来确定用于调用链接器的编译器。这由 target 的源文件的 LANGUAGE 属性确定,并且在静态库的情况下,由依赖库的 LANGUAGE 属性确定。CMake 的选择可以通过 LINKER_LANGUAGE 目标属性覆盖。

工具链特性

CMake 提供了 try_compile() 命令和诸如 CheckSourceCompilesCheckCXXSymbolExistsCheckIncludeFile 等模块来测试各种工具链特性的能力和可用性。这些 API 以某种方式测试工具链,并缓存结果,以便下次 CMake 运行时无需再次执行测试。

一些工具链特性在 CMake 中具有内置处理,不需要编译测试。例如,POSITION_INDEPENDENT_CODE 允许指定目标应作为位置无关代码构建(如果编译器支持该特性)。<LANG>_VISIBILITY_PRESETVISIBILITY_INLINES_HIDDEN 目标属性在编译器支持的情况下添加了隐藏可见性的标志。

交叉编译

如果使用 cmake(1) 命令行参数 --toolchain path/to/file-DCMAKE_TOOLCHAIN_FILE=path/to/file 调用 cmake(1),该文件将尽早加载以设置编译器值。当 CMake 进行交叉编译时,变量 CMAKE_CROSSCOMPILING 被设置为 true。

请注意,在工具链文件中使用 CMAKE_SOURCE_DIRCMAKE_BINARY_DIR 变量通常是不受欢迎的。工具链文件在某些上下文中会使用这些变量,而在其他上下文中(例如,作为 try_compile() 调用的一部分)这些变量可能具有不同的值。在大多数需要评估工具链文件中路径的情况下,更合适的变量是 CMAKE_CURRENT_LIST_DIR,因为它始终具有明确、可预测的值。

为 Linux 交叉编译

典型的 Linux 交叉编译工具链内容如下:

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)

其中

CMAKE_SYSTEM_NAME

是 CMake 用于目标平台的标识符。

CMAKE_SYSTEM_PROCESSOR

是 CMake 用于目标体系结构的标识符。

CMAKE_SYSROOT

是可选的,如果存在 sysroot,则可以指定。

CMAKE_STAGING_PREFIX

也是可选的。它可用于指定主机上的安装路径。CMAKE_INSTALL_PREFIX 始终是运行时安装位置,即使在交叉编译时也是如此。

CMAKE_<LANG>_COMPILER

变量可以设置为完整路径,或在标准位置搜索的编译器名称。对于不支持不带自定义标志或脚本的链接二进制文件的工具链,可以将 CMAKE_TRY_COMPILE_TARGET_TYPE 变量设置为 STATIC_LIBRARY,以告知 CMake 在其检查期间不要尝试链接可执行文件。

CMake 的 find_* 命令默认将在 sysroot 和 CMAKE_FIND_ROOT_PATH 条目中进行搜索,并且还会搜索主机系统根前缀。虽然这可以逐个情况控制,但在交叉编译时,可能需要排除在主机或目标上搜索特定工件。通常,包含文件、库和包应在目标系统前缀中找到,而作为构建一部分必须运行的可执行文件应仅在主机上找到,而不是在目标上找到。这就是 CMAKE_FIND_ROOT_PATH_MODE_* 变量的目的。

为 Cray Linux 环境交叉编译

在 Cray Linux 环境中为计算节点交叉编译无需单独的工具链文件。在 CMake 命令行中指定 -DCMAKE_SYSTEM_NAME=CrayLinuxEnvironment 将确保配置适当的构建设置和搜索路径。该平台将从当前环境变量中提取其配置,并配置项目以使用 Cray Programming Environment 的 PrgEnv-* 模块的编译器包装器(如果存在并已加载)。

Cray Programming Environment 的默认配置仅支持静态库。通过将 CRAYPE_LINK_TYPE 环境变量设置为 dynamic 可以覆盖此设置并启用共享库。

在没有指定 CMAKE_SYSTEM_NAME 的情况下运行 CMake 将以主机模式运行配置步骤,假定为标准的 Linux 环境。如果未被覆盖,最终将使用 PrgEnv-* 编译器包装器,如果目标是登录节点或计算节点,这可能不是期望的行为。例外情况是如果您正在直接在 NID 上构建而不是从登录节点进行交叉编译。如果您尝试为登录节点构建软件,您需要先卸载当前加载的 PrgEnv-* 模块,或者明确告诉 CMake 使用 /usr/bin 中的系统编译器而不是 Cray 包装器。如果您打算改为以计算节点为目标,只需如上所述指定 CMAKE_SYSTEM_NAME

使用 Clang 交叉编译

某些编译器(如 Clang)本质上是交叉编译器。CMAKE_<LANG>_COMPILER_TARGET 可以设置为在编译时向支持的编译器传递一个值。

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

类似地,一些编译器不自带辅助工具(如链接器),但提供了一种指定将由编译器驱动程序使用的外部工具链位置的方法。CMAKE_<LANG>_COMPILER_EXTERNAL_TOOLCHAIN 变量可以在工具链文件中设置,以将路径传递给编译器驱动程序。

为 QNX 交叉编译

与 Clang 编译器一样,QNX QCC 编译器本质上是一个交叉编译器。并且 CMAKE_<LANG>_COMPILER_TARGET 可以设置为在编译时向支持的编译器传递一个值。

set(CMAKE_SYSTEM_NAME QNX)

set(arch gcc_ntoarmv7le)

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

set(CMAKE_SYSROOT $ENV{QNX_TARGET})

为 Windows CE 交叉编译

为 Windows CE 交叉编译需要将相应的 SDK 安装在您的系统上。这些 SDK 通常安装在 C:/Program Files (x86)/Windows CE Tools/SDKs 目录下。

用于 Windows CE 的 Visual Studio 生成器 的工具链文件可能如下所示:

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)

CMAKE_GENERATOR_PLATFORM 告诉生成器使用哪个 SDK。此外,CMAKE_SYSTEM_VERSION 告诉生成器使用哪个版本的 Windows CE。目前开箱即用支持版本 8.0(Windows Embedded Compact 2013)。其他版本可能需要将 CMAKE_GENERATOR_TOOLSET 设置为正确的值。

为 Windows 10 通用应用程序交叉编译

用于 Windows 10 通用应用程序的 Visual Studio 生成器 的工具链文件可能如下所示:

set(CMAKE_SYSTEM_NAME WindowsStore)
set(CMAKE_SYSTEM_VERSION 10.0)

Windows 10 通用应用程序同时针对 Windows Store 和 Windows Phone。将 CMAKE_SYSTEM_VERSION 变量设置为 10.0 或更高。

CMake 会选择一个 Windows SDK,如 CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION 变量文档中所述。

为 Windows Phone 交叉编译

用于 Windows Phone 的 Visual Studio 生成器 的工具链文件可能如下所示:

set(CMAKE_SYSTEM_NAME WindowsPhone)
set(CMAKE_SYSTEM_VERSION 8.1)

为 Windows Store 交叉编译

用于 Windows Store 的 Visual Studio 生成器 的工具链文件可能如下所示:

set(CMAKE_SYSTEM_NAME WindowsStore)
set(CMAKE_SYSTEM_VERSION 8.1)

为 ADSP SHARC/Blackfin 交叉编译

通过将 CMAKE_SYSTEM_NAME 变量设置为 ADSP,并将 CMAKE_SYSTEM_PROCESSOR 变量设置为“零件号”(不带 ADSP- 前缀),例如 21594SC589 等,可以配置 ADSP SHARC 或 Blackfin 的交叉编译。此值不区分大小写。

CMake 将自动在默认安装位置搜索 CCES 或 VDSP++ 安装,并选择找到的最新版本。如果同时安装了 CCES 和 VDSP++,将选择 CCES。可以通过 CMAKE_ADSP_ROOT 变量或 ADSP_ROOT 环境变量设置自定义安装路径。

编译器(cc21k vs. ccblkfn)将根据提供的 CMAKE_SYSTEM_PROCESSOR 值自动选择。

为 Android 交叉编译

通过将 CMAKE_SYSTEM_NAME 变量设置为 Android,可以配置 Android 的交叉编译工具链文件。进一步的配置特定于要使用的 Android 开发环境。

对于 Visual Studio 生成器,CMake 期望安装 NVIDIA Nsight Tegra Visual Studio EditionVisual Studio for Android 工具。有关进一步的配置细节,请参阅这些部分。

对于 Makefile 生成器Ninja 生成器,CMake 期望以下环境之一:

CMake 使用以下步骤来选择环境之一:

  • 如果设置了 CMAKE_ANDROID_NDK 变量,将使用指定位置的 NDK。

  • 否则,如果设置了 CMAKE_ANDROID_STANDALONE_TOOLCHAIN 变量,将使用指定位置的独立工具链。

  • 否则,如果 CMAKE_SYSROOT 变量设置为形如 <ndk>/platforms/android-<api>/arch-<arch> 的目录,则 <ndk> 部分将用作 CMAKE_ANDROID_NDK 的值,并使用 NDK。

  • 否则,如果 CMAKE_SYSROOT 变量设置为形如 <standalone-toolchain>/sysroot 的目录,则 <standalone-toolchain> 部分将用作 CMAKE_ANDROID_STANDALONE_TOOLCHAIN 的值,并使用独立工具链。

  • 否则,如果设置了 cmake 变量 ANDROID_NDK,则它将用作 CMAKE_ANDROID_NDK 的值,并使用 NDK。

  • 否则,如果设置了 cmake 变量 ANDROID_STANDALONE_TOOLCHAIN,则它将用作 CMAKE_ANDROID_STANDALONE_TOOLCHAIN 的值,并使用独立工具链。

  • 否则,如果设置了环境变量 ANDROID_NDK_ROOTANDROID_NDK,则它将用作 CMAKE_ANDROID_NDK 的值,并使用 NDK。

  • 否则,如果设置了环境变量 ANDROID_STANDALONE_TOOLCHAIN,则它将用作 CMAKE_ANDROID_STANDALONE_TOOLCHAIN 的值,并使用独立工具链。

  • 否则,将发出错误诊断,指示未找到 NDK 或独立工具链。

Added in version 3.20: 如果选择了 Android NDK,其版本号将报告在 CMAKE_ANDROID_NDK_VERSION 变量中。

使用 NDK 为 Android 交叉编译

工具链文件可以配置 Makefile 生成器Ninja 生成器Visual Studio 生成器 来为 Android 进行交叉编译。

使用以下变量配置 Android NDK 的使用:

CMAKE_SYSTEM_NAME

设置为 Android。必须指定才能启用 Android 的交叉编译。

CMAKE_SYSTEM_VERSION

设置为 Android API 级别。如果未指定,则值确定如下:

  • 如果设置了 CMAKE_ANDROID_API 变量,则使用其值作为 API 级别。

  • 如果设置了 CMAKE_SYSROOT 变量,则从包含 sysroot 的 NDK 目录结构中检测 API 级别。

  • 否则,将使用 NDK 中可用的最新 API 级别。

CMAKE_ANDROID_ARCH_ABI

设置为 Android ABI(体系结构)。如果未指定,此变量将默认为列表中第一个支持的 ABI:armeabiarmeabi-v7aarm64-v8aCMAKE_ANDROID_ARCH 变量将从 CMAKE_ANDROID_ARCH_ABI 自动计算。另请参阅 CMAKE_ANDROID_ARM_MODECMAKE_ANDROID_ARM_NEON 变量。

CMAKE_ANDROID_NDK

设置为 Android NDK 根目录的绝对路径。如果未指定,将如 上面 所指定的那样选择此变量的默认值。

CMAKE_ANDROID_NDK_DEPRECATED_HEADERS

设置为 true 值以使用已弃用的每个 API 级别的头文件而不是统一头文件。如果未指定,则默认值为 false,除非使用不提供统一头文件的 NDK。

CMAKE_ANDROID_NDK_TOOLCHAIN_VERSION

在 NDK r19 或更高版本上,此变量必须未设置或设置为 clang。在 NDK r18 或更低版本上,将其设置为要选为编译器的 NDK 工具链的版本。如果未指定,默认将是最新可用的 GCC 工具链。

CMAKE_ANDROID_STL_TYPE

设置为指定要使用的 C++ 标准库。如果未指定,将选择一个默认值,如变量文档中所述。

以下变量将被自动计算并提供:

CMAKE_<LANG>_ANDROID_TOOLCHAIN_PREFIX

NDK 工具链中 binutils 的绝对路径前缀。

CMAKE_<LANG>_ANDROID_TOOLCHAIN_SUFFIX

NDK 工具链中 binutils 的主机平台后缀。

例如,一个工具链文件可能包含:

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)

或者,可以在没有工具链文件的情况下指定值:

$ 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

使用独立工具链为 Android 交叉编译

工具链文件可以配置 Makefile 生成器Ninja 生成器,以使用独立工具链为 Android 进行交叉编译。

使用以下变量配置 Android 独立工具链的使用:

CMAKE_SYSTEM_NAME

设置为 Android。必须指定才能启用 Android 的交叉编译。

CMAKE_ANDROID_STANDALONE_TOOLCHAIN

设置为独立工具链根目录的绝对路径。必须存在 ${CMAKE_ANDROID_STANDALONE_TOOLCHAIN}/sysroot 目录。如果未指定,将如 上面 所指定的那样选择此变量的默认值。

CMAKE_ANDROID_ARM_MODE

当独立工具链以 ARM 为目标时,可以选择将其设置为 ON 以目标 32 位 ARM 而不是 16 位 Thumb。有关详细信息,请参阅变量文档。

CMAKE_ANDROID_ARM_NEON

当独立工具链以 ARM v7 为目标时,可以选择将其设置为 ON 以目标 ARM NEON 设备。有关详细信息,请参阅变量文档。

以下变量将被自动计算并提供:

CMAKE_SYSTEM_VERSION

从独立工具链检测到的 Android API 级别。

CMAKE_ANDROID_ARCH_ABI

从独立工具链检测到的 Android ABI。

CMAKE_<LANG>_ANDROID_TOOLCHAIN_PREFIX

独立工具链中 binutils 的绝对路径前缀。

CMAKE_<LANG>_ANDROID_TOOLCHAIN_SUFFIX

独立工具链中 binutils 的主机平台后缀。

例如,一个工具链文件可能包含:

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

或者,可以在没有工具链文件的情况下指定值:

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

使用 NVIDIA Nsight Tegra Visual Studio Edition 为 Android 交叉编译

用于配置 Visual Studio 生成器 以使用 NVIDIA Nsight Tegra 构建 Android 的工具链文件可能如下所示:

set(CMAKE_SYSTEM_NAME Android)

CMAKE_GENERATOR_TOOLSET 可以设置为选择 Nsight Tegra 的“工具链版本”值。

另请参阅目标属性:

为 iOS、tvOS、visionOS 或 watchOS 交叉编译

对于 iOS、tvOS、visionOS 或 watchOS 的交叉编译,推荐使用 Xcode 生成器。也可以使用 Unix MakefilesNinja 生成器,但它们要求项目处理更多领域,如目标 CPU 选择和代码签名。

通过将 CMAKE_SYSTEM_NAME 变量设置为下表中某项值,可以定位任何 Apple 设备平台。默认情况下,将选择最新的设备 SDK。与所有 Apple 平台一样,可以通过设置 CMAKE_OSX_SYSROOT 变量来选择不同的 SDK(例如模拟器),尽管这通常不必要(请参阅下面的 在设备和模拟器之间切换)。可以通过运行 xcodebuild -showsdks 来获取可用 SDK 的列表。

操作系统

CMAKE_SYSTEM_NAME

设备 SDK(默认)

模拟器 SDK

Catalyst SDK

iOS

iOS

iphoneos

iphonesimulator

macosx

tvOS

tvOS

appletvos

appletvsimulator

N/A

visionOS

visionOS

xros

xrsimulator

N/A

watchOS

watchOS

watchos

watchsimulator

N/A

例如,要为 iOS 创建 CMake 配置,以下命令就足够了:

cmake .. -GXcode -DCMAKE_SYSTEM_NAME=iOS

变量 CMAKE_OSX_ARCHITECTURES 可用于为设备和模拟器设置体系结构。变量 CMAKE_OSX_DEPLOYMENT_TARGET 可用于设置 iOS/tvOS/visionOS/watchOS 的部署目标。

下一个示例为 iOS 库安装了五种体系结构,构建了一个通用二进制文件。它在适当的位置添加了相关的 -miphoneos-version-min=9.3-mios-simulator-version-min=9.3 编译器标志。请注意,示例中使用的 CMAKE_IOS_INSTALL_COMBINED 变量现已弃用,因此此方法不再推荐。

$ 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

示例

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

安装

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

检查库

$ 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

代码签名

嵌入式 Apple 平台的一些构建产物需要强制代码签名。如果正在使用 Xcode 生成器,并且需要或希望进行代码签名,则可以通过 CMAKE_XCODE_ATTRIBUTE_DEVELOPMENT_TEAM CMake 变量指定开发团队 ID。然后,此团队 ID 将包含在生成的 Xcode 项目中。默认情况下,CMake 在内部配置阶段(即编译器 ID 和功能检测)避免了代码签名的需要。

在设备和模拟器之间切换

在配置嵌入式平台时,您可以选择目标是真实设备还是模拟器。两者都有自己的独立 SDK,但 CMake 只支持在配置阶段指定一个 SDK。这意味着开发人员必须在配置时选择其中之一。当使用 Xcode 生成器时,这并不是一个很大的限制,因为 Xcode 仍然允许您为设备或模拟器构建,即使配置只为其中一个执行。在 Xcode IDE 中,构建是针对所选的“目标”平台执行的。从命令行构建时,可以通过向底层构建工具(xcodebuild)传递 -sdk 选项直接指定所需的 SDK。例如:

$ cmake --build ... -- -sdk iphonesimulator

请注意,在配置期间进行的检查是针对配置时使用的 SDK 进行的,并且对于其他 SDK 可能不成立。像 find_package()find_library() 等命令会存储和使用仅针对已配置 SDK/平台的详细信息,因此在想要在设备和模拟器构建之间切换时可能会出现问题。您可以遵循以下规则来使设备+模拟器配置正常工作:

  • 使用显式 -l 链接器标志,例如 target_link_libraries(foo PUBLIC "-lz")

  • 使用显式 -framework 链接器标志,例如 target_link_libraries(foo PUBLIC "-framework CoreFoundation")

  • 仅对使用 CMAKE_IOS_INSTALL_COMBINED 功能安装的库使用 find_package()

为 Emscripten 交叉编译

版本 4.2 中添加。

通过将 CMAKE_SYSTEM_NAME 变量设置为 Emscripten,可以配置 Emscripten 的交叉编译工具链文件。例如,一个工具链文件可能包含:

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