交叉編譯
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_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。順帶一提,請注意在 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 會盡最大努力猜測它是否可以在建置機器上運行產生的二進制檔。它會透過查看建置與主機的 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 不會將 el
新增至小端系統的 cpu_family 值結尾。大端和小端 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
搜尋的結果是: