交叉编译

Meson 通过使用交叉构建定义文件,完全支持交叉编译。例如,针对 64 位 Windows 的 GCC/MinGW 交叉编译器,一个最小的交叉构建定义文件 x86_64-w64-mingw32.txt 可以是

[binaries]
c = 'x86_64-w64-mingw32-gcc'
cpp = 'x86_64-w64-mingw32-g++'
ar = 'x86_64-w64-mingw32-ar'
windres = 'x86_64-w64-mingw32-windres'
strip = 'x86_64-w64-mingw32-strip'
exe_wrapper = 'wine64'

[host_machine]
system = 'windows'
cpu_family = 'x86_64'
cpu = 'x86_64'
endian = 'little'

它在 setup 阶段使用。

meson setup --cross-file x86_64-w64-mingw32.txt build-mingw
meson compile -C build-mingw

由于交叉编译比本地构建更复杂,让我们先回顾一下一些术语。传统上,三个最重要的定义被称为 buildhosttarget。这很令人困惑,因为这些术语被用于很多不同的方面。为了简化问题,我们将它们分别称为 构建机器主机目标机器。它们的定义如下

  • 构建机器 是进行实际编译的计算机。
  • 主机 是编译后的二进制文件将运行的机器。
  • 目标机器 是编译后的二进制文件输出将运行的机器,仅当程序生成特定于机器的输出时才有意义

tl/dr 的总结如下:如果您正在进行常规的交叉编译,您只需要关心 build_machinehost_machine。完全忽略 target_machine,您在 99% 的情况下都是正确的。只有编译器和类似的工具关心目标机器。事实上,对于所谓的“多目标”工具来说,目标机器不需要像其他机器那样在构建时固定,而是在运行时选择,因此 target_machine 仍然 不重要。如果您的需求更复杂,或者您对实际细节感兴趣,请继续阅读。

通过示例可能更容易理解。让我们从常规的非交叉编译情况开始。在这些情况下,所有这三台机器都是一样的。到目前为止很简单。

接下来让我们看看最常见的交叉编译设置。假设您使用的是 64 位 OSX 机器,并且正在交叉编译将在 32 位 ARM Linux 板上运行的二进制文件。在这种情况下,您的 构建机器 是 64 位 OSX,您的 主机 是 32 位 ARM Linux,而您的 目标机器 是无关紧要的(但默认为与 主机 相同的值)。这应该也很容易理解。

在这种情况下,通常的错误是将 OSX 系统称为 主机,将 ARM Linux 板称为 目标。这是因为当交叉编译器本身被编译时,它们就是它们实际的名称!假设交叉编译器是在 OSX 上创建的。当这种情况发生时,构建主机 是同一个 OSX,并且与 ARM Linux 目标机器 不同。

简而言之,典型的错误假设术语 构建主机目标 指的是一些固定位置,而实际上它们是相对于当前编译器运行的位置。将 主机 视为当前编译器的 子级,并将 目标 视为可选的 孙级。编译器在创建另一个编译器时不会改变它们的术语,这至少会使它们的用户界面更加复杂。

最复杂的情况是交叉编译交叉编译器。例如,您可以在 Linux 机器上生成一个在 Windows 上运行但在 MIPS Linux 上生成二进制文件的交叉编译器。在这种情况下,构建机器 是 x86 Linux,主机 是 x86 Windows,目标机器 是 MIPS Linux。这种设置被称为 Canadian Cross。顺便说一句,在阅读维基百科或互联网上的交叉编译文章时要小心。它们经常会将构建、主机和目标混淆,即使是在连续的句子中,也会让你困惑,直到你弄明白为止。

再次注意,当您交叉编译某些内容时,用于构建交叉编译器的 3 个系统(构建主机目标)与使用新构建的交叉编译器构建某些内容时使用的系统并不一致。以我们上面提到的加拿大交叉场景为例(为了完整性),由于它的 主机 是 x86 Windows,所以用它构建的任何东西的 构建机器 都是 x86 Windows。由于它的 目标机器 是 MIPS Linux,所以用它构建的任何东西的 主机 都是 MIPS Linux。只有我们用它构建的任何东西的 目标机器 可以由我们自由选择,例如,如果我们想构建另一个在 MIPS Linux 上运行并针对 Aarch64 iOS 的交叉编译器。正如这个例子希望向您表明的那样,机器名称是相对的,并且向左移动了一个位置。

如果您没有理解所有细节,不要担心。对于大多数人来说,需要一段时间才能理解这些概念。不要惊慌,它可能需要一段时间才能理解,但您最终会掌握它。

定义环境

Meson 要求您编写交叉构建定义文件。它定义了交叉构建环境的各种属性。交叉文件包含不同的部分。

交叉文件和本地文件有一些共同的选项,参见此处。假设您已经阅读了该部分,因为本文档只会列出特定于交叉文件的选项。

二进制文件

[binaries]
exe_wrapper = 'wine' # A command used to run generated executables.

exe_wrapper 选项定义了一个 包装器命令,可用于运行此主机的可执行文件。在这种情况下,我们可以使用 Wine,它可以在 Linux 上运行 Windows 应用程序。其他选择包括使用 qemu 或硬件模拟器运行应用程序。如果您有这种包装器,这些行就是您需要编写的全部内容。Meson 会在需要运行主机二进制文件时自动使用给定的包装器。例如,这会在运行项目的测试套件时发生。

属性

除了在 所有机器文件中允许的属性 之外,交叉文件可能包含有关交叉编译器或主机的信息。它看起来像这样

[properties]
sizeof_int = 4
sizeof_wchar_t = 4
sizeof_void* = 4

alignment_char = 1
alignment_void* = 4
alignment_double = 4

has_function_printf = true

sys_root = '/some/path'
pkg_config_libdir = '/some/path/lib/pkgconfig'

在大多数情况下,您不需要大小和对齐设置,Meson 会通过编译和运行一些示例程序来检测所有这些。如果您的构建需要此处未列出的某些数据,Meson 将停止并写下一条错误消息,描述如何解决问题。如果您需要在交叉编译期间使用的额外编译器参数,可以使用 [langname]_args = [args] 设置它们。请记住,将参数指定为数组,而不是单个字符串(即,不要指定为 '-DCROSS=1 -DSOMETHING=3')。

从 0.52.0 开始sys_root 属性可以指向主机系统路径的根目录(将运行编译后二进制文件的系统)。Meson 在内部使用它来设置 pkg-config 的 PKG_CONFIG_SYSROOT_DIR 环境变量。如果未设置,则假定主机系统与构建系统共享一个根目录。

从 0.54.0 开始,pkg_config_libdir 属性可以指向 Meson 在内部用来设置 pkg-config 的 PKG_CONFIG_LIBDIR 环境变量的路径列表。这可以防止 pkg-config 在系统目录中搜索交叉依赖项。

需要注意的一点是,如果您在上一节中没有定义 exe_wrapper,Meson 将尽力猜测它是否可以在构建机器上运行生成的二进制文件。它通过查看构建与主机的 systemcpu_family 来确定这是否可行。但是,在构建机器与主机实际上不兼容的情况下,它们也会匹配。通常,如果构建机器和主机使用的 libc 不兼容,或者代码依赖于构建机器上不可用的内核功能,就会发生这种情况。一个具体的例子是 macOS 构建机器为 iOS 模拟器 x86-64 主机生成二进制文件。它们都是 darwin 并且具有相同的体系结构,但它们的二进制文件实际上不兼容。在这种情况下,您可以使用 needs_exe_wrapper 属性来覆盖自动检测

[properties]
needs_exe_wrapper = true

机器条目

下一部分是主机和目标机器的定义。每个交叉构建定义必须包含其中一个或两个。如果没有,构建将不是交叉构建,而是本地构建。您不需要定义构建机器,因为所有关于它的必要信息都会自动提取。主机和目标机器的定义看起来是一样的。以下是一个主机示例。

[host_machine]
system = 'windows'
subsystem = 'windows'
kernel = 'nt'
cpu_family = 'x86'
cpu = 'i686'
endian = 'little'

这些值足够定义机器以用于交叉编译。相应的目标定义看起来相同,但标题为 target_machine。这些值在您的 Meson 脚本中可用。有三个预定义的变量,分别叫做 build_machinehost_machinetarget_machine。确定主机的操作系统很简单,只需调用 host_machine.system() 即可。从 1.2.0 版本开始,您可以使用 .subsystem().kernel() 方法获取更细粒度的信息。这些函数的返回值在 参考表页面 中有说明。

CPU 有两个不同的值。第一个是 cpu_family。它是一个通用的 CPU 类型。这应该具有来自 CPU 族表 的值。注意 Meson 不会为小端系统在 cpu_family 值末尾添加 el。大端和小端 mips 都只是 mipsendian 字段设置相应的值。

第二个值是 cpu,它是一个更具体的 CPU 子类型。x86 CPU 族的典型值可能包括 i386i586,而 arm 族的典型值可能包括 armv5armv7hl。请注意,CPU 类型字符串是高度依赖于系统的。如果您在同一台机器上但使用不同的操作系统检查其值,您可能会得到不同的值。

如果您没有定义主机,则假设它为构建机器。类似地,如果您没有指定目标机器,则假设它为主机。

启动交叉构建

拥有交叉文件后,启动构建很简单

$ meson setup builddir --cross-file cross_file.txt

完成配置后,通过以通常的方式调用 meson compile 来启动编译。

内省和系统检查

meson 对象提供两个函数来确定交叉编译状态。

meson.is_cross_build()        # returns true when cross compiling
meson.can_run_host_binaries() # returns true if the host binaries can be run, either with a wrapper or natively

您可以在系统编译器或交叉编译器上运行系统检查。您只需指定要使用哪一个。

build_compiler = meson.get_compiler('c', native : true)
host_compiler = meson.get_compiler('c', native : false)

build_int_size = build_compiler.sizeof('int')
host_int_size  = host_compiler.sizeof('int')

混合主机和构建目标

有时您需要构建一个用于生成源文件的工具。然后针对实际目标编译它们。为此,您需要使用系统的本地编译器构建一些目标。这只需要一个额外的关键字参数。

native_exe = executable('mygen', 'mygen.c', native : true)

然后您可以使用 native_exe 作为生成器规则的一部分或任何其他您可能想要的内容。

使用自定义标准库

有时在交叉编译中,您需要构建自己的标准库,而不是使用编译器提供的标准库。Meson 内置支持透明地切换标准库。在交叉文件中使用的调用如下

[properties]
c_stdlib = ['mylibc', 'mylibc_dep'] # Subproject name, variable name

这指定 C 标准库在 Meson 子项目 mylibc 中的内部依赖变量 mylibc_dep 中提供。它用于整个源树(包括子项目)中的每个交叉构建的 C 目标,并且标准库被禁用。这些目标的构建定义不需要任何修改。

请注意,它支持任何语言,不仅是 c,使用 <lang>_stdlib 属性。

0.56.0 版本开始,变量名参数不再是必需的,只要子项目调用 meson.override_dependency('c_stdlib', mylibc_dep)。上面的例子就变成了

[properties]
c_stdlib = 'mylibc'

更改交叉编译文件设置

交叉编译文件设置只在构建目录第一次设置时读取。之后的任何更改都会被忽略。这与普通编译一样,一旦构建树设置好,就不能更改编译器。如果你需要编辑交叉编译文件,你需要清空构建树并从头开始创建。

自定义数据

你可以在 properties 中存储任意数据,并从 Meson 文件中访问它们。例如,如果你的交叉编译文件有以下内容

[properties]
somekey = 'somevalue'

然后你可以使用 meson 对象像这样访问它

myvar = meson.get_external_property('somekey')
# myvar now has the value 'somevalue'

交叉编译文件位置

从 0.44.0 版本开始,Meson 支持从系统位置加载交叉编译文件(Windows 除外)。它将是 $XDG_DATA_DIRS/meson/cross,或者如果 XDG_DATA_DIRS 未定义,则依次尝试 /usr/local/share/meson/cross 和 /usr/share/meson/cross,用于系统范围的交叉编译文件。用户本地文件可以放在 $XDG_DATA_HOME/meson/cross,或者如果它未定义,则放在 ~/.local/share/meson/cross。

尝试的位置顺序如下

  • 本地目录的相对文件
  • 用户本地位置
  • 系统范围位置,按顺序

鼓励发行版将交叉编译文件与他们的交叉编译器工具链包一起发布,或者作为独立包发布,并将它们放在上面提到的系统路径之一中。

这些文件可以在不向交叉编译文件添加路径的情况下自动加载。例如,如果 ~/.local/share/meson/cross 包含一个名为 x86-linux 的文件,那么以下命令将使用该交叉编译文件开始交叉编译。

meson setup builddir/ --cross-file x86-linux

搜索结果为