cmake-toolchains(7)

简介

CMake 使用工具链中的实用程序来编译、链接库、创建归档文件,以及其他任务来驱动构建。 可用的工具链实用程序由启用的语言决定。 在正常构建中,CMake 会根据系统自检和默认值自动确定主机构建的工具链。 在交叉编译情况下,可以使用工具链文件指定有关编译器和实用程序路径的信息。

在版本 3.19 中添加: 可以使用 cmake-presets(7) 指定工具链文件。

语言

语言通过 project() 命令启用。 语言特定的内置变量,例如 CMAKE_CXX_COMPILERCMAKE_CXX_COMPILER_ID 等,通过调用 project() 命令来设置。 如果顶级 CMakeLists 文件中没有项目命令,则将隐式生成一个命令。 默认情况下,启用的语言为 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 目标属性添加隐藏可见性的标志(如果编译器支持)。

交叉编译

如果 cmake(1) 使用命令行参数 --toolchain path/to/file-DCMAKE_TOOLCHAIN_FILE=path/to/file 调用,则该文件将在早期加载,以设置编译器的值。 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 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 应用商店和 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 应用商店交叉编译

为 Windows 应用商店配置 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 环境变量来设置。

编译器(cc21kccblkfn)将根据提供的 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 或独立工具链。

在版本 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 变量已设置,则 API 级别将从包含 sysroot 的 NDK 目录结构中检测到。

  • 否则,将使用 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 生成器 之一以使用针对 Android 的 NVIDIA Nsight Tegra 进行构建的工具链文件可能如下所示:

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