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 源文件属性决定,在静态库的情况下,由依赖库的 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 条目,以及查找主机系统根前缀。尽管这可以根据具体情况进行控制,但在交叉编译时,排除在主机或目标中查找特定构件可能很有用。通常,包含文件、库和包应在目标系统前缀中找到,而必须作为构建一部分运行的可执行文件应仅在主机上找到,而不在目标上找到。这就是 CMAKE_FIND_ROOT_PATH_MODE_* 变量的目的。

Cray Linux 环境交叉编译

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

Cray 编程环境的默认配置是只支持静态库。可以通过将 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 通用应用程序交叉编译

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

set(CMAKE_SYSTEM_NAME WindowsStore)
set(CMAKE_SYSTEM_VERSION 10.0)

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

CMake 根据 CMAKE_VS_WINDOWS_TARGET_PLATFORM_VERSION 变量的文档描述选择 Windows SDK。

Windows Phone 交叉编译

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

set(CMAKE_SYSTEM_NAME WindowsPhone)
set(CMAKE_SYSTEM_VERSION 8.1)

Windows Store 交叉编译

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

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++。自定义安装路径可以通过 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 EditionAndroid 版 Visual Studio 工具。有关更多配置详细信息,请参阅这些部分。

对于 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")

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