cmake-toolchains(7)

简介

CMake 使用实用工具链来编译、链接库、创建归档文件以及执行其他任务来驱动构建。可用的工具链实用工具由启用的语言决定。在正常构建中,CMake 基于系统自省和默认值自动确定主机构建的工具链。在交叉编译场景中,可以指定一个工具链文件,其中包含有关编译器和实用工具路径的信息。

3.19 版本新增: 可以使用 cmake-presets(7) 来指定工具链文件。

语言

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

project(C_Only C)

特殊值 NONE 也可以与 project() 命令一起使用,以禁用所有语言

project(MyProject NONE)

enable_language() 命令可用于在 project() 命令之后启用语言

enable_language(CXX)

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

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

变量和属性

以下几个变量与已启用的工具链的语言组件相关

CMAKE_<LANG>_COMPILER

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

CMAKE_<LANG>_COMPILER_ID

CMake 使用的编译器标识符

CMAKE_<LANG>_COMPILER_VERSION

编译器的版本。

CMAKE_<LANG>_FLAGS

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

CMake 需要一种方法来确定使用哪个编译器来调用链接器。这由 LANGUAGE 属性(target 的源文件)和静态库的情况下的依赖库的 LANGUAGE 决定。CMake 所做的选择可以使用 LINKER_LANGUAGE 目标属性覆盖。

工具链特性

CMake 提供了 try_compile() 命令和包装宏,例如 CheckSourceCompilesCheckCXXSymbolExistsCheckIncludeFile,以测试各种工具链特性的能力和可用性。这些 API 以某种方式测试工具链并缓存结果,以便下次 CMake 运行时不必再次执行测试。

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

交叉编译

如果使用命令行参数 --toolchain path/to/file-DCMAKE_TOOLCHAIN_FILE=path/to/file 调用 cmake(1),则会提前加载该文件以设置编译器的值。CMAKE_CROSSCOMPILING 变量在 CMake 进行交叉编译时设置为 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 条目中查找所有情况,以及在主机系统根前缀中查找。尽管可以根据具体情况进行控制,但在交叉编译时,排除在主机或目标中查找特定工件可能很有用。通常,应在目标系统前缀中找到 include、库和软件包,而应仅在主机上而不是在目标上找到必须作为构建一部分运行的可执行文件。这是 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- 前缀)来配置 ADSP SHARC 或 Blackfin 的交叉编译,例如 21594SC589 等。此值不区分大小写。

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

编译器(cc21kccblkfn)会根据提供的 CMAKE_SYSTEM_PROCESSOR 值自动选择。

Android 交叉编译

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

对于 Visual Studio 生成器,CMake 期望安装 NVIDIA Nsight Tegra Visual Studio EditionVisual Studio 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 或独立工具链。

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(架构)。如果未指定,此变量将默认为 armeabiarmeabi-v7aarm64-v8a 列表中第一个受支持的 ABI。CMAKE_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 中,构建是为选定的“目标”平台执行的。从命令行构建时,可以通过将 -sdk 选项传递给底层构建工具 (xcodebuild) 来直接指定所需的 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()