CMake 交叉编译

Dori Exterman
Dori Exterman / 2月 26 2021
CMake 交叉编译

想知道“魔笛手”在这里能发挥什么作用吗?想象一下,把 CMake 当做法力高强的魔笛手,C++ 的项目则是故事中的那些被魔笛手拯救的孩子。

 

父母要抚养一个孩子并非易事,营养需要面面俱到,保证身体健康,关心事无巨细,确保快乐成长。其难度不亚于 C++ 项目的交叉编译,但一个保姆就能让事情变得更简单。类似地,我们也有 CMake 来简化交叉编译中的烦琐事。

 

交叉编译?到底是什么?

以一个著名的应用程序为例,比如  Microsoft Paint。在它成为可执行文件之前,是一组源代码文件。编译源代码并将目标代码链接到单个可执行文件,这是编译器的工作。在正常情况下:

Cross-compile

Microsoft Paint  的源代码是一组C++文件,在Microsoft windows 中使用Visual Studio 编译器进行编译。现在来看交叉编译场景:

Cross-compile

因此,当源代码编译器对应的操作系统与其当前所在的操作系统不同时,就会发生交叉编译。这张图应该更清楚一点:

Cross-compile

现在,交叉编译这个术语从何而来已经很明显了。跨平台软件开发并不容易,因为每个操作系统都有自己的特点。为 Windows 编写的源代码通常不能为 Linux 编译,反之亦然。这就是 Qt 等框架和 POSIX 等标准发挥作用的地方。

 

我不能忍受简单编译吗?

不总是这样。举个例子,一个开发者正在为 Android 开发的应用程序,他会利用开发机器(Windows、Linux 或 Mac)的计算能力为 Android 交叉编译应用程序。如果开发人员将目标锁定为 Raspberry Pi 这样的平台,情况也是如此。比起要在一台低性能机器上耗费大量时间运行编译,在功能强大的开发机器上设置交叉编译环境并将二进制文件复制到 Raspberry Pi,则容易得多。

 

交叉编译的另一个用例,是使用目标机器的本机编译器生成依赖项的复杂性。例如,以 Chromium(Microsoft Edge 浏览器和 Google Chrome 浏览器的基础)这样一个在多个平台上都受支持的开源项目为例。由于存在多个依赖项,因此不建议尝试以本机方式构建 Chromium。更确切地说,本地镜像是一个 Linux 容器,承载 GNU GCC 来交叉编译源代码。

 

能举个交叉编译的例子吗?

Windows 10 桌面操作系统有两种不同的风格:

  • Windows 10, Intel CPUs ( 32 Bit 和 64 Bit 版本)
  • Windows 10,ARM CPUs (ARM32 和ARM64 Bit 版本)

为 Intel CPU 编译的程序与 ARM CPU 不兼容,反之亦然。Microsoft Visual Studio 附带以下命令工具,其中一些用于交叉编译:

  • VS2015 x64 ARM Cross Tools Command
  • VS2015 x64 Native Tools Command
  • VS2015 x64 x86 Cross Tools Command
  • VS2015 x86 ARM Cross Tools Command
  • VS2015 x86 Native Tools Command
  • VS2015 x86 x64 Cross Tools Command

 

我们对交叉编译工具很感兴趣。让我们试着创建一个简单的 “Hello, World!” C++ 程序:

#include <iostream>

int main(int argc, char** argv)

{

std::cout << “Hello, World!” << std::endl;

return 0;

}

 

在 x64 本机命令行上,编译上述程序可以得Cross-compile

在 x64 ARM cross tools 命令提示符下,编译相同的程序会给出:Cross-compile

特别注意 /machine:arm 输出。编译成功并生成了可执行文件,但从运行可执行文件可以看出,它不适用于当前计算机:

Cross-compile

恭喜!你已经在不同的机器上成功交叉编译了最简单 C++ 程序。

 

为什么需要交叉编译?

为同一操作系统支持的不同 CPU 进行编译是交叉编译的最简单形式。如果你想使用 Linux 并希望生成支持 Windows 的可执行文件,该怎么办?如果可能的话,最好使用相同的代码库来生成 Linux 和 Windows 可执行文件。

 

我们来试一下。我有一个 Windows 10 的机器,运行了一个适用于 Linux 的Windows 子系统,并且在这个系统上安装了 Ubuntu。简单输入:

sudo apt-get install mingw-w64

 

将为 Windows 安装 mingw 交叉编译器工具链。我们创建 CMakeLists.txt 文件:

# set minimum cmake version

cmake_minimum_required(VERSION 3.5 FATAL_ERROR)

 

# project name and language

project(HelloWorld LANGUAGES CXX)

 

set(CMAKE_CXX_STANDARD 11)

set(CMAKE_CXX_EXTENSIONS OFF)

set(CMAKE_CXX_STANDARD_REQUIRED ON)

 

include(GNUInstallDirs)

set(CMAKE_ARCHIVE_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})

set(CMAKE_LIBRARY_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_LIBDIR})

set(CMAKE_RUNTIME_OUTPUT_DIRECTORY ${CMAKE_BINARY_DIR}/${CMAKE_INSTALL_BINDIR})

 

# define executable and its source file

add_executable(HelloWorld main.cpp)

 

为CMake创建一个工具链文件,它告诉我们一些关于交叉编译工具链的信息。像这样:

 

# the name of the target operating system

set(CMAKE_SYSTEM_NAME Windows)

 

# which compilers to use

set(CMAKE_C_COMPILER i686-w64-mingw32-gcc)

set(CMAKE_CXX_COMPILER i686-w64-mingw32-g++)

 

set(CMAKE_FIND_ROOT_PATH /usr/i686-w64-mingw32)

 

# adjust the default behavior of the find commands:

# search headers and libraries in the target environment

set(CMAKE_FIND_ROOT_PATH_MODE_INCLUDE ONLY)

set(CMAKE_FIND_ROOT_PATH_MODE_LIBRARY ONLY)

 

# search programs in the host environment

set(CMAKE_FIND_ROOT_PATH_MODE_PROGRAM NEVER)

 

将此文件另存为 cross-compilation.cmake,并将其保存在 CMakeLists.txt 文件(见上文)和 Main.cpp. 创建一个名为“build”的新文件夹并更改到该目录。现在发出命令:

 

cmake .. -DCMAKE_TOOLCHAIN_FILE=../cross-compilation.cmake

cmake –build .

 

这将会创建一个 HelloWorld.exe,就像 build/bin 文件夹中的 CMakeLists.txt 中指定的那样。我们取得了什么成就?我们为 Windows 创建了一个完全在 Linux 下运行的可执行文件。不同的工具链文件和编译器应允许相同的 CMakeLists.txt 文件为多个平台创建的目标。

 

使用 CMake 进行跨平台软件开发

CMake 是一个很好的跨平台软件开发工具。它使用一组称为工具链的实用程序来驱动构建。在构建中使用 CMake 有两种主要场景:

  • CMake 负责选择工具链的普通构建
  • 用户指定工具链文件的跨平台构建

 

上面的简单示例演示了如何创建工具链文件,并通知 CMake 使用该工具链文件来驱动构建。在实际的跨平台软件开发中,相关工作人员会仔细选择有助于这种开发的框架,例如 Qt

另外,如果你想了解更多关于 CMake 的信息,请阅读我们的博客 CMake vs Make

 

结论

魔笛手的故事有一个圆满的结局吗?一些版本说魔笛手把孩子们带到了一个美丽的地方。CMake 作为跨平台软件开发的一个选择,也应该带来这样一个美好的结局。所以,最后的结局会是怎么样呢?

Speed_up_cpp

订阅博客

阅读 Incredibuild 独家内容

Dori Exterman

Dori Exterman 是一名软件开发专家、产品策略分析师,在软件开发行业拥有 20 年的工作经历。作为 Incredibuild 的首席技术官(CTO),他指导公司的产品策略,负责规划产品前景、执行方案、选择技术合作伙伴。在加入 Incredibuild 之前,Dori 在软件公司身兼数职,主要负责各种技术和产品开发,聚焦系统架构、产品性能、先端技术、DevOps、发布管理和 C++.他是开发工具先进技术领域的专家和分享者。