建構 Clang 工具鍊

Fuchsia 將 Clang 做為官方編譯器。

必要條件

您需要 CMake 3.13.4 以上版本才能執行這些指令。這是建構 LLVM 的最低版本

雖然 CMake 支援不同的建構系統,但建議使用 Ninja

您在 Fuchsia 結帳時,兩者都必須以預建的形式提供。下列指令假設 cmakeninja 位於您的 PATH 中:

export PATH=${FUCHSIA_DIR}/prebuilt/third_party/cmake/${platform}/bin:${PATH}
export PATH=${FUCHSIA_DIR}/prebuilt/third_party/ninja/${platform}/bin:${PATH}

其中 ${FUCHSIA_DIR} 是指 Fuchsia 來源樹狀結構的根目錄。

正在取得來源

下列範例指令使用 ${LLVM_SRCDIR} 來參照 LLVM 來源樹狀結構結帳的根層級。您可以使用由 LLVM 社群維護的官方單一存放區 https://github.com/llvm/llvm-project

LLVM_SRCDIR=${HOME}/llvm/llvm-project
git clone https://github.com/llvm/llvm-project ${LLVM_SRCDIR}
cd ${LLVM_SRCDIR}
git checkout ${REVISON_NUMBER}

紫紅色

建構隨工具鍊建構的執行階段程式庫之前,您需要具備 Fuchsia IDK (舊稱 SDK)。IDK 必須位於 ${IDK_DIR} 變數指向的目錄中:

IDK_DIR=${HOME}/fuchsia-idk

如要下載最新的 IDK,您可以使用下列程式碼:

# For Linux
cipd install fuchsia/sdk/core/linux-amd64 latest -root ${IDK_DIR}

# For macOS
cipd install fuchsia/sdk/core/mac-amd64 latest -root ${IDK_DIR}

為 Fuchsia IDK 產生 RISC-V 程式庫和 Sysroot

如要為 Fuchsia 建構 RISC-V LLVM 執行階段程式庫,您需要為 Fuchsia IDK 產生 RISC-V 程式庫和 sysroot。

由於指令碼會變更 ${IDK_DIR}/pkg/sysroot/meta.json 的內容,因此我們需要將這個檔案設為可寫入:

chmod 644 "${IDK_DIR}/pkg/sysroot/meta.json"

下一步是執行指令碼,產生 RISC-V 程式庫和 sysroot:

python3 ${FUCHSIA_DIR}/scripts/clang/generate_sysroot.py --sdk-dir=${IDK_DIR} \
  --arch=riscv64 \
  --ifs-path=${FUCHSIA_DIR}/prebuilt/third_party/clang/${platform}/bin/llvm-ifs

若為 Linux x64 平台,${platform} 應為 linux-x64,而在 Mac x64 平台上,則 ${platform} 應為 mac-x64

Linux 適用的 Sysroot

如要加入 Linux 適用的編譯器執行階段和 C++ 程式庫,請下載 sysroot。 這個檔案必須位於 ${SYSROOT_DIR} 變數指向的目錄中。

SYSROOT_DIR=${HOME}/fuchsia-sysroot/

如要下載 sysroot,可使用以下程式碼:

cipd install fuchsia/third_party/sysroot/linux integration -root ${SYSROOT_DIR}

建構適用於 Fuchsia 的 Clang 工具鍊

Clang CMake 建構系統支援啟動 (又稱為多階段) 版本。Fuchsia 針對 Clang 編譯器使用雙階段 Bootstrap 版本。不過,對於工具鍊相關的開發,建議使用單一階段建構

如果您的目標是使用 Clang 進行實驗,那麼單一階段版本應可滿足您的需求。第一個階段編譯器是僅限主機的編譯器,具有第二階段所需的部分選項集。第二階段編譯器是全面最佳化的編譯器,目的是向使用者提供。

設定這些編譯器需要許多選項。為了簡化 Fuchsia Clang 建構設定,包含在 Clang 程式碼集 (Fuchsia.cmakeFuchsia-stage2.cmake) 的 CMake 快取檔案中。

mkdir llvm-build
mkdir llvm-install  # For placing stripped binaries here
INSTALL_DIR=${pwd}/llvm-install
cd llvm-build

單一階段建構 Fuchsia 設定

開發 Fuchsia 的 Clang 時,您可以使用快取檔案測試 Fuchsia 設定,但只在啟用 LTO 的情況下執行第二個階段,如此一來,建構時間會比用於漸進式開發作業更快,因此您不必手動指定所有選項:

cmake -G Ninja -DCMAKE_BUILD_TYPE=Debug \
  -DCMAKE_TOOLCHAIN_FILE=${FUCHSIA_DIR}/scripts/clang/ToolChain.cmake \
  -DUSE_GOMA=ON \
  -DLLVM_ENABLE_LTO=OFF \
  -DLINUX_x86_64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR} \
  -DLINUX_aarch64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR} \
  -DFUCHSIA_SDK=${IDK_DIR} \
  -DCMAKE_INSTALL_PREFIX= \
  -C ${LLVM_SRCDIR}/clang/cmake/caches/Fuchsia-stage2.cmake \
  ${LLVM_SRCDIR}/llvm
ninja toolchain-distribution  -j1000  # Build the distribution

如果上述方法因出現與 Ninja 相關的錯誤而失敗,您可能需要將 ninja 新增至 PATH。您可以在 ${FUCHSIA_DIR}//prebuilt/third_party/ninja/${platform}/bin 找到預先建構的執行檔。

ninja toolchain-distribution 應足以建構所有二進位檔,但 Fuchsia 版本會假設部分程式庫遭到移除,因此需要使用 ninja install-toolchain-distribution-stripped

雙階段建構 Fuchsia 設定

這大致上與在實際工作環境建構工具中執行的內容相同,且用於建構 Fuchsia 運送給使用者的工具鍊。

cmake -GNinja \
  -DCMAKE_TOOLCHAIN_FILE=${FUCHSIA_DIR}/scripts/clang/ToolChain.cmake \
  -DUSE_GOMA=ON \
  -DCMAKE_INSTALL_PREFIX= \
  -DSTAGE2_LINUX_aarch64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR} \
  -DSTAGE2_LINUX_x86_64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR} \
  -DSTAGE2_FUCHSIA_SDK=${IDK_DIR} \
  -C ${LLVM_SRCDIR}/clang/cmake/caches/Fuchsia.cmake \
  ${LLVM_SRCDIR}/llvm
ninja stage2-toolchain-distribution -j1000
DESTDIR=${INSTALL_DIR} ninja stage2-install-toolchain-distribution-stripped -j1000

Runtime.json

如果 Fuchsia 建構作業因缺少 runtime.json 檔案而失敗,您必須執行下列指令產生新的 runtime.json 檔案:

python3 ${FUCHSIA_DIR}/scripts/clang/generate_runtimes.py  \
  --clang-prefix ${INSTALL_DIR} --sdk-dir ${IDK_DIR}          \
  --build-id-dir ${INSTALL_DIR}/lib/.build-id > ${INSTALL_DIR}/lib/runtime.json

產生的檔案含有 Fuchsia 版本用於瞭解工具鍊各項程式庫的相對路徑。

要點總整理

用於建構單一階段工具鍊的複製貼上程式碼。這個程式碼可在 LLVM 建構目錄中執行,並假設為 Linux 環境。

cd ${LLVM_BUILD_DIR}  # The directory your toolchain will be installed in

# Environment setup
FUCHSIA_DIR=${HOME}/fuchsia/  # Replace with wherever Fuchsia lives
LLVM_SRCDIR=${HOME}/llvm/llvm-project  # Replace with wherever llvm-project lives
IDK_DIR=${HOME}/fuchsia-idk/
SYSROOT_DIR=${HOME}/fuchsia-sysroot/
CLANG_TOOLCHAIN_PREFIX=${FUCHSIA_DIR}/prebuilt/third_party/clang/linux-x64/bin/
GOMA_DIR=${FUCHSIA_DIR}/prebuilt/third_party/goma/linux-x64/

# Download necessary dependencies
cipd install fuchsia/sdk/core/linux-amd64 latest -root ${IDK_DIR}
cipd install fuchsia/third_party/sysroot/linux integration -root ${SYSROOT_DIR}

# CMake invocation
cmake -G Ninja -DCMAKE_BUILD_TYPE=Release \
  -DCMAKE_TOOLCHAIN_FILE=${FUCHSIA_DIR}/scripts/clang/ToolChain.cmake \
  -DUSE_GOMA=ON \
  -DLLVM_ENABLE_LTO=OFF \
  -DLINUX_x86_64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR} \
  -DLINUX_aarch64-unknown-linux-gnu_SYSROOT=${SYSROOT_DIR} \
  -DFUCHSIA_SDK=${IDK_DIR} \
  -DCMAKE_INSTALL_PREFIX= \
  -C ${LLVM_SRCDIR}/clang/cmake/caches/Fuchsia-stage2.cmake \
  ${LLVM_SRCDIR}/llvm

# Build and strip binaries and place them in the install directory
ninja toolchain-distribution -j1000
DESTDIR=${INSTALL_DIR} ninja install-toolchain-distribution-stripped -j1000

# Generate runtime.json

python3 ${FUCHSIA_DIR}/scripts/clang/generate_runtimes.py    \
  --clang-prefix ${INSTALL_DIR} --sdk-dir ${IDK_DIR}            \
  --build-id-dir ${INSTALL_DIR}/lib/.build-id > ${INSTALL_DIR}/lib/runtime.json

使用自訂 Clang 建構 Fuchsia

如要指定用於建構 Fuchsia 的自訂 clang 工具鍊,請將 --args clang_prefix=\"${INSTALL_DIR}/bin\" --no-goma 傳遞至 fx set 指令,然後執行 fx build

fx set core.x64 --args=clang_prefix=\"${INSTALL_DIR}/bin\" --no-goma
fx build

這個檔案包含 Fuchsia 版本用於瞭解工具鍊各程式庫所在位置的相對路徑。

開發 Clang

開發 Clang 時,您可能會想要使用更適合用於漸進式開發及快速處理時間的設定。

建構 LLVM 最簡單的方式就是使用下列指令:

cmake -GNinja \
  -DCMAKE_BUILD_TYPE=Debug \
  -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld" \
  ${LLVM_SRCDIR}/llvm
ninja

您可以使用 LLVM_ENABLE_PROJECTS 變數啟用其他專案。如要啟用所有常見專案,您會使用:

  -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld;compiler-rt;libcxx;libcxxabi;libunwind"

同樣地,您也可以讓某些專案以執行階段的形式建構,這表示這些專案將使用剛剛建構的程式,而非主機編譯器:

  -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld" \
  -DLLVM_ENABLE_RUNTIMES="compiler-rt;libcxx;libcxxabi;libunwind" \

LLVM_ENABLE_PROJECTSLLVM_ENABLE_RUNTIMES 都已在 CMake 快取檔案中設定,因此除非您要明確新增更多專案或執行階段,否則通常不需要設定這些項目。

Clang 是大型專案,編譯器的效能也十分重要。如要縮短建構時間,建議使用 Clang 做為主機編譯器。如果可以,請將 LLD 做為主機連接器使用。建議使用 LTO 建構,如要達到最佳效能,也可使用設定檔引導最佳化 (PGO) 功能。

如要將主機編譯器設為 Clang,並將主機連接器設為 LLD,您可以使用下列額外標記:

  -DCMAKE_C_COMPILER=${CLANG_TOOLCHAIN_PREFIX}clang \
  -DCMAKE_CXX_COMPILER=${CLANG_TOOLCHAIN_PREFIX}clang++ \
  -DLLVM_ENABLE_LLD=ON

這項操作假設 ${CLANG_TOOLCHAIN_PREFIX} 會指向 Clang 安裝項目的 bin 目錄,並在結尾加上斜線 (因為這個 Make 變數用於 Zircon 版本)。例如,如要使用 Fuchsia 結帳 (Linux) 中的編譯器:

CLANG_TOOLCHAIN_PREFIX=${FUCHSIA_DIR}/prebuilt/third_party/clang/linux-x64/bin/

消毒液

只要將 LLVM_USE_SANITIZER=<sanitizer name> 新增至 CMake 叫用,即可將大部分的清理程式用於 LLVM 工具。不過有些 LLVM 工具會觸發誤判情形 因此 MSan 很特別如要使用 MSan 支援進行建構,您必須先透過 MSan 支援建構 libc++。您可以在同一個版本中執行此操作。如要設定採用 MSan 支援的建構,請先使用 LLVM_USE_SANITIZER=MemoryLLVM_ENABLE_LIBCXX=ON 執行 CMake。

cmake -GNinja \
  -DCMAKE_BUILD_TYPE=Debug \
  -DCMAKE_C_COMPILER=${CLANG_TOOLCHAIN_PREFIX}clang \
  -DCMAKE_CXX_COMPILER=${CLANG_TOOLCHAIN_PREFIX}clang++ \
  -DLLVM_ENABLE_PROJECTS="clang;clang-tools-extra;lld;libcxx;libcxxabi;libunwind" \
  -DLLVM_USE_SANITIZER=Memory \
  -DLLVM_ENABLE_LIBCXX=ON \
  -DLLVM_ENABLE_LLD=ON \
  ${LLVM_SRCDIR}/llvm

您通常將會執行 Ninja,但我們希望使用經淨化的 libc++ 版本建構所有內容,但如果現在建構,系統會使用來自 ${CLANG_TOOLCHAIN_PREFIX} 的 libc++,而未經淨化。所以我們先只建構 cxx 和 cxxabi 目標。當工具以動態方式連結至 libcxx 時,這些變數會用於取代 ${CLANG_TOOLCHAIN_PREFIX} 中的這些項目

ninja cxx cxxabi

現在您有了經過處理的 libc++ 版本,您可以將建構設定為使用這個版本,而非使用 ${CLANG_TOOLCHAIN_PREFIX} 中的版本,然後建構所有內容。

ninja

總結:

cmake -GNinja \
  -DCMAKE_BUILD_TYPE=Debug \
  -DCMAKE_C_COMPILER=${CLANG_TOOLCHAIN_PREFIX}clang \
  -DCMAKE_CXX_COMPILER=${CLANG_TOOLCHAIN_PREFIX}clang++ \
  -DLLVM_USE_SANITIZER=Address \
  -DLLVM_ENABLE_LIBCXX=ON \
  -DLLVM_ENABLE_LLD=ON \
  ${LLVM_SRCDIR}/llvm
ninja libcxx libcxxabi
ninja

測試 Clang

如要執行 Clang 測試,您可以使用 check-<component> 目標:

ninja check-llvm check-clang

所有測試都可以使用 check-all 執行,但請注意,這項作業可能需要大量時間,視您在建構作業中啟用的專案數量而定。

如果只要測試一項特定測試,您可以使用環境變數 LIT_FILTER。如果測試路徑為 clang/test/subpath/testname.cpp,您可以使用:

LIT_FILTER=testname.cpp ninja check-clang

指定不同的 check-<component>,同樣也能以相同的技巧執行其他子專案的測試。

從 CAS 下載工具鍊

我們的 Clang Toolchain CI 建構工具會將所有建構構件上傳至內容 Addressed Storage (CAS)。這可讓您輕鬆快速下載特定工具鍊,而不必從頭開始建立。這可以大幅加快工具鍊問題的調查速度,因為除了建構 Fuchsia 之外,LLVM 的建構時間也可以縮短。

以下範例說明如何從頭開始安裝 cas 工具,並將特定工具鍊下載至語料庫目錄中:

$ cipd install infra/tools/luci/cas/linux-amd64 latest -root luci
$ ./luci/cas download -cas-instance chromium-swarm -digest \
    ad53e1f315a849955190594fde6b07e11e76b40563db5779fcc69d6a6e04dc71/267 -dir corpus

在上述範例中,-digest 欄位會傳遞專屬 ID,供 cas 工具用於擷取正確的構件。您可以從 Fuchsia 的 CI 建構工具取得 digest,方法是選取要取得工具鍊的來源建構工具,接著展開 clang->cas->archive 欄位,然後按一下 CAS_UI 連結。 畫面上隨即會顯示 CAS 上傳作業的一些相關資訊,包括摘要。

實用的 CMake 旗標

還有許多其他 CMake 旗標可用於建構,但其中一些對於建構工具鍊可能相當實用。

增加可同時 (本機) 執行的連結工作數量。連結工作數量取決於 RAM 大小。如果是 LTO 版本,每項工作至少需要 10 GB。

其他資源

說明文件:

談話: