交叉编译
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
由于交叉编译比本地构建更复杂,让我们先回顾一下一些术语。传统上,三个最重要的定义被称为 build、host 和 target。这很令人困惑,因为这些术语被用于很多不同的方面。为了简化问题,我们将它们分别称为 构建机器、主机 和 目标机器。它们的定义如下
- 构建机器 是进行实际编译的计算机。
- 主机 是编译后的二进制文件将运行的机器。
- 目标机器 是编译后的二进制文件输出将运行的机器,仅当程序生成特定于机器的输出时才有意义。
tl/dr
的总结如下:如果您正在进行常规的交叉编译,您只需要关心 build_machine
和 host_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 将尽力猜测它是否可以在构建机器上运行生成的二进制文件。它通过查看构建与主机的 system
和 cpu_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_machine
、host_machine
和 target_machine
。确定主机的操作系统很简单,只需调用 host_machine.system()
即可。从 1.2.0 版本开始,您可以使用 .subsystem()
和 .kernel()
方法获取更细粒度的信息。这些函数的返回值在 参考表页面 中有说明。
CPU 有两个不同的值。第一个是 cpu_family
。它是一个通用的 CPU 类型。这应该具有来自 CPU 族表 的值。注意 Meson 不会为小端系统在 cpu_family 值末尾添加 el
。大端和小端 mips 都只是 mips
,endian
字段设置相应的值。
第二个值是 cpu
,它是一个更具体的 CPU 子类型。x86
CPU 族的典型值可能包括 i386
或 i586
,而 arm
族的典型值可能包括 armv5
或 armv7hl
。请注意,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
搜索结果为