cmake如果不设置交叉编译,默认情况下,会使用主机系统(运行 cmake 命令的操作系统)的编译器来编译我们的工程,那么得到的可执行文件或库文件只能在 Ubuntu 系统运行,如果我们需要使得编译得到的可执行文件或库文件能够在ARM 平台上运行,则需要配置交叉编译,本文将进行介绍。我们使用的交叉编译器如下:
arm-poky-linux-gnueabi-gcc #C 编译器
arm-poky-linux-gnueabi-g++ #C++编译器
其实配置交叉编译非常简单,只需要设置几个变量即可,如下所示:
# 配置 ARM 交叉编译
set(CMAKE_SYSTEM_NAME Linux) #设置目标系统名字
set(CMAKE_SYSTEM_PROCESSOR arm) #设置目标处理器架构
# 指定编译器的 sysroot 路径
set(TOOLCHAIN_DIR /opt/fsl-imx-x11/4.1.15-2.1.0/sysroots)
set(CMAKE_SYSROOT ${TOOLCHAIN_DIR}/cortexa7hf-neon-poky-linux-gnueabi)
# 指定交叉编译器 arm-gcc 和 arm-g++
set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-g++)
# 为编译器添加编译选项
set(CMAKE_C_FLAGS "-march=armv7ve -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7")
set(CMAKE_CXX_FLAGS "-march=armv7ve -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
CMAKE_SYSTEM_NAME 变量在前面给大家介绍过,表示目标主机(譬如 ARM 开发板)的操作系统名称,这里将其设置为 Linux,表示目标操作系统是 Linux 系统。 CMAKE_SYSTEM_ PROCESSOR 变量表示目标架构名称。
CMAKE_SYSROOT 变量前面也给大家介绍过,该变量的值会传递给 gcc 编译器的--sysroot 选项,也就是--sysroot=${CMAKE_SYSROOT},--sysroot 选项指定了编译器的 sysroot 目录,也就是编译器的系统根目录,编译过程中需要链接的库、头文件等,就会去该目录下寻找,譬如标准 C 库、标准 C 头文件这些。
CMAKE_C_COMPILER 变量指定了 C 语言编译器 gcc,由于是交叉编译,所以应该指定为 arm-gcc。
CMAKE_CXX_COMPILER 变量指定了 C++语言编译器 g++,由于是交叉编译,所以应该指定为 arm-g++。
CMAKE_C_FLAGS 变量为 gcc 编译器添加编译选项,CMAKE_CXX_FLAGS 变量为 g++编译器添加编译选项。
CMAKE_FIND_ROOT_PATH_MODE_LIBRARY 和 CMAKE_FIND_ROOT_PATH_MODE_INCLUDE 被设置为 ONLY;CMAKE_FIND_ROOT_PATH_MODE_INCLUDE 变量控制 CMAKE_SYSROOT 中的路径 是否被 find_file()和 find_path()使用。如果设置为 ONLY,则只会搜索 CMAKE_SYSROOT 中的路径,如果设置为 NEVER,则 CMAKE_SYSROOT 中的路径将被忽略并且仅使用主机系统路径。如果设置为 BOTH, 则将搜索主机系统路径和 CMAKE_SYSROOT 中的路径。
同理,CMAKE_FIND_ROOT_PATH_MODE_LIBRARY 变量控制 CMAKE_SYSROOT 中的路径是否被 find_library()使用,如果设置为 ONLY,则只会搜索 CMAKE_SYSROOT 中的路径,如果设置为 NEVER, 则 CMAKE_SYSROOT 中的路径将被忽略并且仅使用主机系统路径。如果设置为 BOTH,则将搜索主机系统路径和 CMAKE_SYSROOT 中的路径。
CMAKE_SYSROOT、CMAKE_C_COMPILER、CMAKE_CXX_COMPILER 这些变量涉及到交叉编译工具的安装路径,需要根据自己的实际安装路径来确定。
接着我们进行测试,譬如工程目录结构如下所示:
├── build
├── CMakeLists.txt
└── main.c
main.c 源文件中调用了 printf()函数打印了“Hello World!”字符串,CMakeLists.txt 内容如下:
# CMakeLists.txt
cmake_minimum_required(VERSION 3.5)
##################################
# 配置 ARM 交叉编译
#################################
set(CMAKE_SYSTEM_NAME Linux) #设置目标系统名字
set(CMAKE_SYSTEM_PROCESSOR arm) #设置目标处理器架构
# 指定编译器的 sysroot 路径
set(TOOLCHAIN_DIR /opt/fsl-imx-x11/4.1.15-2.1.0/sysroots)
set(CMAKE_SYSROOT ${TOOLCHAIN_DIR}/cortexa7hf-neon-poky-linux-gnueabi)
# 指定交叉编译器 arm-linux-gcc 和 arm-linux-g++
set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-g++)
# 为编译器添加编译选项
set(CMAKE_C_FLAGS "-march=armv7ve -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7")
set(CMAKE_CXX_FLAGS "-march=armv7ve -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
#################################
# end
##################################
project(HELLO) #设置工程名称
add_executable(main main.c)
这里要注意,配置 ARM 交叉编译的这些代码需要放置在 project()命令之前,否则不会生效! 接着进入到 build 目录下,然后执行 cmake,此时执行 cmake 会报错!如下所示:
原因是使用了过低版本的cmake,笔者用的是 Ubuntu 系统自带的 cmake 工具,版本是 3.5.1,导致这里出错,所以笔者建议大家去下载一个高版本的 cmake,然后使用这个高版本的 cmake 工具,不然会报错。
那怎么去下载高版本的 cmake,其实非常简单,我们首先进入到 cmake 的 GitHub 链接地址 Releases · Kitware/CMake · GitHub,此处下载使用了 3.16.0版本,如下所示:
这里我们下载 cmake-3.16.0-Linux-x86_64.tar.gz 压缩包文件,这个不是 cmake 的源码工程,而是可以在 x86-64 的 Linux 系统下运行的可执行程序,其中就包括了 cmake 工具,所以我们下载这个即可,非常方便, 都不用自己编译!
下载成功之后将其拷贝到 Ubuntu 系统的用户家目录下,并将其解压到某个目录,解压之后生成 cmake-3.16.0-Linux-x86_64 文件夹,这里笔者选择将其解压到家目录下的 tools 目录中。
cmake 工具就在 cmake-3.16.0-Linux-x86_64/bin 目录下。 现在重新进入到我们的工程目录下,进入到 build 目录执行 cmake即可。
编译生成的 main 可执行文件,通过 file 命令查看可知,它是一个 ARM 架构的可执行程序。
上例中的这种交叉编译配置方式自然是没有问题的,但是不规范,通常的做法是,将这些配置项(也就是变量的设置)单独拿出来写在一个单独的配置文件中,而不直接写入到 CMakeLists. txt源码中,然后在执行 cmake 命令时,指定配置文件给 cmake,让它去配置交叉编译环境。
通过如下方式指定配置文件:
cmake -DCMAKE_TOOLCHAIN_FILE=cfg_file_path ..
通过-DCMAKE_TOOLCHAIN_FILE 选项指定配置文件,-D 是 cmake 命令提供的一个选项,通过该选项可以创建一个缓存变量(缓存变量就是全局变量,在整个工程中都是生效的,会覆盖 CMakeLists.txt 源码中定义的同名变量 ),所以 -DCMAKE_TOOLCHAIN_FILE 其实就是设置了缓存变量 CMAKE_TOOLCHAIN_FILE,它的值就是“=”号后面的内容,cmake 会执行 CMAKE_TOOLCHAIN_FILE 变量所指定的源文件,对交叉编译进行设置;现在我们进行测试,在工程源码目录下创建一个配置文件 arm-linux-setup.cmake,内容如下:
##################################
# 配置 ARM 交叉编译
#################################
set(CMAKE_SYSTEM_NAME Linux) #设置目标系统名字
set(CMAKE_SYSTEM_PROCESSOR arm) #设置目标处理器架构
# 指定编译器的 sysroot 路径
set(TOOLCHAIN_DIR /opt/fsl-imx-x11/4.1.15-2.1.0/sysroots)
set(CMAKE_SYSROOT ${TOOLCHAIN_DIR}/cortexa7hf-neon-poky-linux-gnueabi)
# 指定交叉编译器 arm-linux-gcc 和 arm-linux-g++
set(CMAKE_C_COMPILER ${TOOLCHAIN_DIR}/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-gcc)
set(CMAKE_CXX_COMPILER ${TOOLCHAIN_DIR}/x86_64-pokysdk-linux/usr/bin/arm-poky-linux-gnueabi/arm-poky-linux-gnueabi-g++)
# 为编译器添加编译选项
set(CMAKE_C_FLAGS "-march=armv7ve -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7")
set(CMAKE_CXX_FLAGS "-march=armv7ve -mfpu=neon -mfloat-abi=hard -mcpu=cortex-a7")
set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)
set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)
set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)
#################################
# end
##################################
推荐使用这种方式配置交叉编译,而不是直接写入到 CMakeLists.txt 源码中。