2024-02-19

CMake 和 VcPkg 的跨平台编译

编译与翻译很像,把人容易理解的语言翻译成为机器能读懂并去执行的语言。每天都在使用 c++ 来写代码代码,然后通过编译器将我写的这些代码翻译成为机器可以听得懂的指令来执行。不同类型的 CPU 又使用着不同的指令,比如 Intel 架构的 CPU 使用 X86 指令,而 ARM 又使用 ARM 指令。跨平台编译就是指在某一个指令平台 A 上编译出另一个指令平台 B 的代码。编译出来的代码虽然是在 A 平台上被生成出来,但是却只能在 B 平台上运行。

目标平台也许无法生成自己平台的代码

我们在 Windows 平台上写代码,然后通过所属语言的编译器将代码编译成为机器可执行的代码,并交由当前的机器执行,这一般都是正常的写代码的流程。这是一个完美的世界,在 A 平台上运行 A 平台的编译器编译出 A 平台的代码并且在 A 平台上执行。但是,并不是所有的情况都能这么完美。例如,有一些家用电器上需要使用一些智能系统,它可能只能跑 ARM 指令的代码,而且这些智能系统,因为受到内存或 CPU 及存储设备等周边设施的限制,无法允许一个完整的编译器在其平台上运行,这时候我们就需要另一台电脑上将运行在该智能系统的代码编译出来,也就是在 A 平台(Windows/Linux/macOS)上编译出 B 平台(ARM 等)的代码。

VcPkg + CMake + Cross compile

跨平台的概念其实很简单,就是找一个在 A 平台上跑的编译器,其可以生成 B 平台的代码即可。而跨平台编译 c++ 的代码,则找一个对应的目标平台的编译器来编译 c++ 代码即可。在 CMake 中,可以通过设置 CMAKE_CXX_COMPILER 环境变量来达到这一目的。比如,我将 CMAKE_CXX_COMPILER 的值设置为 /usr/bin/mingw32-x86_64-gcc,那么在整个代码的编译过程中,CMake 都会使用 mingw32 的 c++ 编译器来生成能运行在 Windows 上的二进制文件。

Vcpkg 中管理着各种各样的 c++ 库,这些库同时也包含有各平台下的编译二进制目标,我们只要在使用跨平台编译的时候下载到对应的平台的库并将其链接到我们的目标代码中,即可以一边享受 VcPkg 带来的便利性,又不失跨平台的灵活。

步骤

目标平台相关的信息会被放到一个叫做 ToolChain 的 CMake 文件中,这个文件中包含了目标平台所需要使用到的编译器的名称。然后在启动 CMake 来配置时,需要将 CMAKE_TOOLCHAIN_FILE 这个环境变量设置成为这个 ToolChain 文件的路径。但是在使用 VcPkg 的时候,我们需要将这个环境变量指向 VcPkg 自己的脚本文件,相当于让 VcPkg 取得 CMake 的脚本生成工作的控制权。但是不要担心,VcPkg 同时提供了一个名为 VCPKG_CHAINLOAD_TOOLCHAIN_FILE 的环境变量,使用该变量来指向具体的 ToolChain 文件就可以了。而 VcPkg 已经在它自己的脚本里为大家准备好了各种平台的 ToolChain 文件,只需要在 [vcpkg root]/scripts/toolchains 里找到合适的文件即可。

因为我都喜欢把 VcPkg 当作 c++ 项目的子项目,所以我都会在 CMake 文件中指定 VcPkg 的 ToolChain 文件,所以不会在命令行里再重新指定。

我们还需要告诉 VcPkg 当前的目标平台是什么,于是需要指定 VCPKG_TARGET_TRIPLET 的值,这些值可以在 [vcpkg root]/triplets 里找到。VcPkg 在编译的时候会将目标平台的二进制文件自动下载到本地。

最后,还需要告诉 VcPkg 目标架构是什么,例如,目标若是 x64 平台,那么需要指定环境变量 VCPKG_TARGET_ARCHITECTURE 的值为 x64。通过 VCPKG_HOST_TRIPLET 来指定使用什么平台来编译目标代码,通过设置 VCPKG_HOST_TRIPLET 的值来指定。

例如,要在 x64 的 Linux 系统下编译出 Windows 平台的 x64 的代码,可以使用如下的命令。(注意我将 VcPkg 作为子模块添加到了当前的项目中)

1
cmake . -B build -DVCPKG_TARGET_TRIPLET=x64-mingw-static -DVCPKG_CHAINLOAD_TOOLCHAIN_FILE=`pwd`/vcpkg/scripts/toolchains/mingw.cmake -DVCPKG_HOST_TRIPLET=x64-linux -DVCPKG_TARGET_ARCHITECTURE=x64

后记

没有想到会那么简单,这回使用了 VcPkg 来管理所有的工程依赖,然后还可以跨平台输出代码,这回可以安心的只在 Linux 写代码,然后编译出来给各平台使用,不需要再纠结 Windows 下的编译问题,也不需要再使用为 Visual Studio 来单独配置一套工程文件。对于我这种整天浸泡在 c++ 编译问题的人来说,真的是要省掉非常多的不必要的麻烦。

MATTHEW
桂ICP备17005075号