交叉编译指的在一个平台上生成另一个平台上的可执行代码。很多时候,开发的代码可能并不只是在开发主机的平台上执行,比如在Windows下开发的程序,希望能够在Linux、或者MacOS下执行;或者有时候目标平台根本就没有操作系统,没有对应的编译器,所以必然需要进行交叉编译。
交叉编译笔者在工作中经常会遇到。笔者开发使用的宿主机一般都是Linux(Ubuntu),交叉编译的目的基本都是为了将程序代码编译成目标嵌入式平台的库文件,然后再在目标系统上调用集成,一般都是ARM系列的CPU。而对于不同操作系统(Windows、Linux、MacOS)之间的交叉编译,则主要是为了编译示例程序,方便在不同操作系统上进行展示或者调试。
一 编译和CMake工具链
就像在前面的文章GCC编译过程概述中说的一样,目标文件的编译构建,其实是有很多个阶段的,每个阶段会使用不同的工具,比如特定语言的编译器、链接器、库文件操作工具等等,这也是GCC
被称为编译工具套件的原因。
一般情况下,对于GCC
来说,只需要使用命令行工具gcc
或者g++
就可以完成大部分工作,很多步骤都是被封装好的,套件内的不同工具会被适时地自动调用。
在CMake中,也是类似的概念,这些编译构建工具统称为工具链(toolchain),工具链基于不同的语言有不同配置,一般情况下,CMake会根据宿主机自动选择应该使用的工具链。
在进行交叉编译时,需要显式指定一个工具链文件,指明要使用的编译器、编译器的配置选项以及其他必要工具的路径。
1 工具链文件
工具链文件(toolchain file)习惯上一般以.toolchain.cmake
结尾。工具链文件的编写并不复杂,一般只需要指定以下这些CMake内置变量:
-
CMAKE_SYSTEM_NAME:表明目标系统名称,比如
Linux
、QNX
、Android
等;如果目标平台并没有系统,则应该指定为Generic
;默认值为CMAKE_HOST_SYSTEM_NAME
; -
CMAKE_SYSTEM_PROCESSOR:表明目标平台架构,比如最常见的
arm
; -
CMAKE_<LANG>_COMPILER:表明特定语言的要采用的编译器,需要使用完整路径,一般需要设置
C语言
编译器变量CMAKE_C_COMPILER
和C++
编译器变量CMAKE_CXX_COMPILER
; -
CMAKE_<LANG>_FLAGS:设置特定编译器对应的编译选项,也可以使用
add_compile_options
为所有编译器设置相同的一些编译选项。
还有一些是可选的参数,根据目标平台的不同按需设置即可,大多数时候,cmake执行的时候会报错提示的~
下面是一个使用arm-none-eabi-gcc
编译器的工具链文件示例:
对于编译选项,需要结合项目实际开发情况。对于arm
平台,比较重要的几个选项包括:
-
-mcpu
-
-mfpu
-
-mfloat-abi
其他更多参数可以在GCC官网或者对应编译器官方查看。
这里是假定知道了arm-none-eabi-gcc
所在路径,对于团队协作可能并不是很友好,写死的路径在不同开发者的机器上可能不一样。当然,对于编译都只在服务器进行的情况就没有关系了。
如果开发者将编译套件对应可执行文件目录添加到系统环境变量,那么也可以直接设置对应的编译器可执行文件名称,比如:
也可以通过find_program
在系统路径下查找及指定的路径下查找对应的编译器可执行文件,再进行设置:
二 CMake交叉编译
在需要进行交叉编译的时候,先编写一个适用于目标平台的工具链文件,然后在执行cmake
命令开始构建时,可以使用参数--toolchain
或者-DCMAKE_TOOLCHAIN_FILE=
指定工具链文件的路径即可:
所以交叉编译是比较简单的,一般只需要知道使用什么编译套件以及对应的配置。
如果是交叉编译给Android平台使用的库,直接使用Android官方NDK中提供的toolchain就好。
使用Android toolchain路径一定要通过NDK所在目录指定:
<NDK>/build/cmake/android.toolchain.cmake
,因为它会使用相对路径访问NDK内部其他的资源,否则会出错。