CMake 交叉编译

本文通过一个简单的示例,演示如何在 CMake 工程中添加交叉编译的配置,实现编译不同硬件平台的可执行文件。所有代码均可在 getiot/linux-c 仓库找到。

搭建工程

hello.c 文件

#include <stdio.h>

int main(void)
{
    printf("Hello, GetIoT\n");
    return 0;
}

CMakeLists.txt 文件

cmake_minimum_required(VERSION 3.1.3)
project(hello)
set( HELLO_SRCS hello.c)
add_executable({PROJECT_NAME}{HELLO_SRCS})

执行下面命令编译 x86-64 平台的可执行文件。

$ mkdir build
$ cd build
$ cmake .. && make

查看可执行文件描述:

$ file hello 
hello: ELF 64-bit LSB shared object, x86-64, version 1 (SYSV), dynamically linked, interpreter /lib64/ld-linux-x86-64.so.2, BuildID[sha1]=5f92608723d5b497b773b56b366f4dada31c2318, for GNU/Linux 3.2.0, not stripped

方法一:增加选项

在 CMakeLists.txt 中通过 option 指令增加一个 CROSSCOMPILE_ENABLED 编译选项,默认为 OFF,当设置为 ON 时将进行交叉编译。以 aarch64 作为目标平台为例,修改后的 CMakeLists.txt 文件如下。

cmake_minimum_required(VERSION 3.1.3)

project(hello)

set( HELLO_SRCS 
    hello.c
)

option( CROSSCOMPILE_ENABLED "Whether to build for arm" OFF)

if (CROSSCOMPILE_ENABLED)
    set(CMAKE_SYSTEM_NAME Linux)
    set(CMAKE_SYSTEM_PROCESSOR aarch64)
    set(target_arch aarch64-linux-gnu)
    set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc)
    set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++)
    set(CMAKE_LIBRARY_ARCHITECTURE {target_arch} CACHE STRING "" FORCE)
    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)
endif()

add_executable({PROJECT_NAME} ${HELLO_SRCS})

编译时添加 CROSSCOMPILE_ENABLED 选项配置,如下:

$ cmake -DCROSSCOMPILE_ENABLED=ON ..
$ make

查看可执行文件描述:

$ file hello 
hello: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=cc40eabf8479fde33c1d87e49431047dfdaee240, for GNU/Linux 3.7.0, not stripped

方法二:指定文件

在不修改 CMakeLists.txt 文件的情况下,可以新增一个 arm_linux_setup.cmake 文件,将交叉编译的相关配置写在这里。例如:

set(CMAKE_SYSTEM_NAME Linux)
set(CMAKE_SYSTEM_PROCESSOR aarch64)
set(target_arch aarch64-linux-gnu)
set(CMAKE_C_COMPILER /usr/bin/aarch64-linux-gnu-gcc)
set(CMAKE_CXX_COMPILER /usr/bin/aarch64-linux-gnu-g++)
set(CMAKE_LIBRARY_ARCHITECTURE ${target_arch} CACHE STRING "" FORCE)
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 的变量 CMAKE_TOOLCHAIN_FILE 来指定工具链文件,命令如下。

$ cmake -DCMAKE_TOOLCHAIN_FILE=../arm_linux_setup.cmake ..
$ make

查看可执行文件描述:

$ file hello 
hello: ELF 64-bit LSB shared object, ARM aarch64, version 1 (SYSV), dynamically linked, interpreter /lib/ld-linux-aarch64.so.1, BuildID[sha1]=cc40eabf8479fde33c1d87e49431047dfdaee240, for GNU/Linux 3.7.0, not stripped

配置说明

对于交叉编译,CMake 并不知道目标系统是什么,所以我们需要设置一些 CMake 变量来告诉 CMake。

  • CMAKE_SYSTEM_NAME:即目标系统名,这里是 Linux
  • CMAKE_SYSTEM_PROCESSOR:目标系统的处理器名,这里是 aarch64

对于工具链,则是通过下面两个变量来定位:

  • CMAKE_C_COMPILER:C 编译器的可执行文件的路径
  • CMAKE_CXX_COMPILER:C++ 编译器的可执行文件的路径

实际上,上述这些变量可以在调用 CMake 时通过命令行传递,不过这样做很容易出错,而且用起来不方便。所以我们前面通过 option 选项和指定工具链文件的方式,将相关的变量传递给 CMake,这样用起来就方便很多。