GN 工具鍊和 Fuchsia Build

GN 工具鍊總覽

GN 建構工具可讓一個建構作業使用多個工具鍊,以不同方式編譯相同目標。

每個 toolchain() 執行個體都會對應至:

  • 專屬名稱,以 GN 標籤表示。

    舉例來說,「//build/toolchain/fuchsia:x64」會命名 //build/toolchain/fuchsia/BUILD.gn 檔案中定義的工具鍊例項,並使用 toolchain("x64") 定義。

  • 一組用於編譯原始碼和連結二進位檔的指令和建構旗標

    舉例來說,使用一個工具鍊叫用 Clang,另一個工具鍊叫用 Microsoft Visual C++,即可讓單一建構作業使用這兩套編譯器產生二進位檔。

  • 建構圖表節點命名空間

    使用相同 GN 路徑,但以不同工具鍊執行個體編譯的目標。這會反映在完整 GN 標籤的格式中,看起來像 "//<dir>:<target>(<toolchain_dir>:<toolchain_target>)"

    舉例來說,使用 //toolchain:debug 工具鍊的指令編譯時,//src/foo:bar(//toolchain:debug) 會對應至 //src/foo/BUILD.gn 中定義的 bar 目標。

  • 獨立的 GN 執行環境

    每個工具鍊執行個體都會剖析 GN buildconfig 檔案,為該工具鍊中定義目標的所有規則設定全域變數和預設值。

    在實務上,如果使用兩種不同的工具鍊建構相同目標,系統會剖析對應的 BUILD.gn 檔案兩次,但每次都會使用一組不同的全域變數、預設設定和 BUILDCONFIG.gn 中定義的自訂範本。

  • 目標輸出的個別根目錄

    在預設工具鍊中建構的目標會放在 root_build_dir 下方,而使用 //<toolchain_dir>:<toolchain_name> 執行個體建構的目標則會放在 ${root_build_dir}/<toolchain_name> 下方。

    這個位置可在 GN 生成時間透過 root_out_dir 變數取得。

至少會有一條工具鍊 (稱為「預設工具鍊」),這是透過從 buildconfig 檔案呼叫 set_default_toolchain()來決定。

詳情請參閱toolchain()參考說明文件。

Fuchsia 建構作業如何使用 GN 工具鍊

Fuchsia 建構作業會以多種方式使用 GN 工具鍊:

  • 建構主機和裝置可執行檔

    目前,建構作業會定義 //build/toolchain/fuchsia:x64//build/toolchain/fuchsia:arm64,以便為 64 位元 Intel 和 ARM 架構建構 Fuchsia 可執行二進位檔。

    此外,它也會定義 //build/toolchain:host_x64,為主體機器 (即建構發生的位置) 建構程式碼。

    此外,即使主機未執行這些架構,它也會定義 //build/toolchain:linux_x64//build/toolchain:linux_arm64,以產生 Linux 64 位元程式碼。

    此外,還有許多專用工具鍊可用於編譯開機載入程式和部分核心,稍後會說明。

  • 建構 ELF 共用程式庫

    在 Fuchsia 上,進入共用物件的機器碼 (即 GN 語言中的 shared_library()loadable_module() 執行個體) 必須使用 -fPIC 編譯器和連結器選項建構。

    這與可執行程式碼不同,可執行程式碼會改用 -fPIE

    為解決這個問題,我們定義了個別的工具鍊例項,用於編譯共用程式庫的程式碼。

    詳情請參閱「ELF 共用程式庫重新導向」。

  • 建構不同變數 (例如插碼或最佳化) 的二進位檔

    Fuchsia 建構作業支援多個「建構變種版本」,可稍微不同的方式建構機器碼,例如:

    • asanubsan 變體分別用於透過 Clang 的 Address Sanitizer 和 Undefined Behaviour Sanitizer 建構機器碼。甚至還有結合兩者的 asan-ubsan 變體。

    • coverage 變數用於建構機器碼,並啟用 Clang 的檢測式剖析功能,以支援程式碼涵蓋率收集作業。

    • profile 變體也用於建構插碼程式碼,但目的是支援設定檔引導最佳化。

    • thinltolto 變數用於建構二進位檔,並啟用連結時間最佳化。

    • gcc 變體用於使用 GCC 編譯器 (而非 Clang) 建構特定 Zircon 核心片段,有助於找出可能以非常重要的方式影響核心的細微機器碼產生問題。

    建構作業的 BUILDCONFIG.gn 檔案中也定義了許多其他變體。

  • 產生 (或處理) 來源檔案

    建構作業需要在許多地方產生原始檔,供其他目標使用。舉例來說,系統會處理 FIDL 通訊協定定義檔,為各種語言 (C++、Rust、Go 和 Dart) 產生繫結,供其他 source_set() 或類似目標使用。

    由於使用這些來源的目標可以在不同的工具鍊執行個體中定義,因此確保這項產生作業只執行一次 (而非每個工具鍊執行個體執行一次),會很有幫助,因為在所有情況下,輸出內容都會完全相同。

    因此,Fuchsia 建構會定義 fidling 工具鍊,以執行 FIDL 繫結生成作業。請注意,這個工具鍊只會用於透過 action() 目標執行幾個指令碼,絕不會實際編譯這些指令碼。

    同樣地,建構作業中也定義了許多其他「基本」工具鍊,可執行不應重複的處理工作。

Fuchsia 建構作業如何定義 toolchain() 執行個體。

Fuchsia 建構作業提供這些範本,可定義具有各種功能的 toolchain() 執行個體:

  • basic_toolchain() 定義「基本」工具鍊,也就是只以 copy()action() 為目標,且永遠不需要使用 GN 內建的 C++ 和 Rust 編譯支援。

    這些項目用於產生輸出內容,供其他工具鍊 (例如語言繫結) 中的其他目標使用,避免重複作業。

    請注意,由於 GN 完全不支援這些語言,因此會使用一個基本工具鍊來建構 Go 二進位檔,另一個則用於建構 Dart 二進位檔。

  • clang_toolchain() 會定義叫用 Clang 編譯器的工具鍊執行個體。支援使用 GN 的內建規則建構 C++ 和 Rust 來源。

    支援的目標平台包括 Fuchsia、Linux、Win32 PE/COFF (UEFI 啟動載入程式需要此平台),甚至是 WebAssembly!

  • clang_toolchain_suite() 會根據目前的建構變數設定,定義一或多個工具鍊執行個體。建議您使用這個方法,而不是直接呼叫 clang_toolchain(),因為這樣才能讓建構變體正常運作。

  • clang_host_toolchain_suite() 用於產生主體機器的機器碼的工具鍊。

  • zircon_toolchain() 定義可用於建構 Zircon 核心、開機載入程式,甚至是 C 程式庫部分的工具鍊執行個體。這些二進位檔通常需要非標準的編譯和連結器指令 (例如不同的 ABI,或缺少標準連結環境)。

    注意:`zircon_toolchain() 建立的工具鍊不支援 Rust 程式設計語言。

    這個範本的一項顯著功能是,它也支援使用 GCC 編譯器 (而非 Clang) 建構二進位檔。這項功能已證明有助於找出核心非常敏感的低階程式碼生成問題。

    注意:我們不打算支援使用 GCC 建構平台其餘部分。

  • zircon_toolchain_suite() 用於根據目前的建構變數設定,定義一或多個工具鍊執行個體。相較於直接呼叫 zircon_toolchain(),建議使用這個方法。

請注意,zircon_toolchain()clang_toolchain() 的區別主要是歷史因素,未來可能會合併為通用範本。