CMAKE_TOOLCHAIN_FILE是唯一推荐的交叉编译入口,必须用独立工具链文件而非硬编码编译器路径或在CMakeLists.txt中设置;其核心包括CMAKE_SYSTEM_NAME、CMAKE_SYSTEM_PROCESSOR、编译器路径及CMAKE_FIND_ROOT_PATH及其MODE变量,确保正确查找目标平台头文件和库。

CMAKE_TOOLCHAIN_FILE 是唯一推荐的入门入口,硬编码编译器路径或在 CMakeLists.txt 里设 CMAKE_C_COMPILER 属于临时凑合,长期维护会出问题。
必须用工具链文件,不能写进 CMakeLists.txt
把交叉编译配置塞进 CMakeLists.txt 看似简单,但会导致几个实际问题:
-
externalproject_add()完全不继承这些变量,子项目直接 fallback 到宿主编译器 - 多平台切换时要反复改源码,容易误提交、漏改
-
CMAKE_SYSTEM_NAME和CMAKE_SYSTEM_PROCESSOR必须在project()前生效,而 CMake 的变量作用域规则会让它在某些版本里静默失效
正确做法是单独建一个 arm-linux.cmake(名字随意),内容只包含目标平台声明和工具链路径:
set(CMAKE_SYSTEM_NAME Linux) set(CMAKE_SYSTEM_PROCESSOR arm) set(CMAKE_C_COMPILER arm-linux-gnueabihf-gcc) set(CMAKE_CXX_COMPILER arm-linux-gnueabihf-g++) set(CMAKE_FIND_ROOT_PATH /opt/arm-rootfs)
然后调用:cmake -DCMAKE_TOOLCHAIN_FILE=arm-linux.cmake -B build -S .
CMAKE_FIND_ROOT_PATH_MODE_* 决定头文件和库能不能找到
没配这组变量,find_package(OpenSSL) 或 find_library(crypto) 很可能找到宿主机的 x86 库,链接时失败或运行时报段错误。
典型组合是:
-
CMAKE_FIND_ROOT_PATH_MODE_PROGRAM设为NEVER:只在宿主机找pkg-config、cmake这类构建工具 -
CMAKE_FIND_ROOT_PATH_MODE_LIBRARY和CMAKE_FIND_ROOT_PATH_MODE_INCLUDE都设为ONLY:强制只从CMAKE_FIND_ROOT_PATH下找库和头文件
漏掉任一 mode 设置,CMake 就会混合搜索宿主机和目标机路径,结果不可控。
交叉编译时 CMAKE_CROSSCOMPILING 自动为 TRUE,但别依赖它做逻辑分支
CMAKE_CROSSCOMPILING 确实会在 CMAKE_SYSTEM_NAME 设置后自动变成 TRUE,但它只是个信号量,不是环境隔离开关。
常见误区是这么写:
if(CMAKE_CROSSCOMPILING) add_definitions(-DUSE_ARM_OPTIMIZATIONS) endif()
问题在于:这个宏会被传给宿主机上的 configure_file()、execute_process() 或生成代码的脚本,导致它们行为异常。真正该用条件的地方,是 target_compile_options() 或 target_link_libraries() 这类最终作用于目标二进制的命令。
验证是否真交叉成功,看 file 输出和符号表
编译完别急着拷到板子上跑,先本地验证:
-
file build/myapp必须显示类似ELF 32-bit LSB shared object, ARM, EABI5 version 1,而不是x86-64 -
readelf -A build/myapp | grep Tag_ABI_VFP_args能看到 ARM 特有属性才说明浮点 ABI 生效 - 如果用了
find_package(Threads)却链接了宿主机的libpthread.so.0,ldd build/myapp会报not a dynamic executable或路径错乱
最容易被忽略的是 CMAKE_FIND_ROOT_PATH 没指向完整 rootfs —— 缺 /usr/include 或 /lib 子目录,find_path() 就会静默失败,转而用宿主机头文件,编译通过但运行崩溃。


















