交叉編譯

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

由於交叉編譯比原生建置更複雜,讓我們先來複習一些術語。傳統上,三個最重要的定義稱為建置主機目標。這會造成混淆,因為這些術語用於許多不同的事物。為了簡化問題,我們將這些稱為建置機器主機機器目標機器。它們的定義如下

  • 建置機器是執行實際編譯的電腦。
  • 主機機器是編譯後的二進制檔將在其上執行的機器。
  • 目標機器是編譯後的二進制檔的輸出將在其上執行的機器,僅在程式產生特定於機器的輸出時才有意義。

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。順帶一提,請注意在 Wikipedia 或一般網路閱讀關於交叉編譯的文章。它們很常見地會將建置、主機和目標混淆,即使在連續的句子中也是如此,這可能會讓您感到困惑,直到您弄清楚為止。

再次注意,當您交叉編譯某些東西時,在建置交叉編譯器時使用的 3 個系統(建置主機目標)與使用新建立的交叉編譯器建置某些東西時使用的系統不一致。為了從上面(為了完全通用性)採用我們的 Canadian Cross 情境,由於它的主機機器是 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 不會將 el 新增至小端系統的 cpu_family 值結尾。大端和小端 mips 都只是 mips,並適當地設定 endian 欄位。

第二個值是 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

搜尋的結果是: