使用 GN 工具鍊的最佳做法

總覽

在 GN 中,工具鍊可讓您以多種方式建立目標。為了瞭解 以及對 GN 程式碼偵錯,您必須瞭解自己使用的工具鍊。由於 GN 代碼 可以是 current_toolchain 上的條件式,也就是執行一項操作 工具鍊 A 可能會在工具鍊 B 中執行完全不同的工作;且 該工具可能完全存在於工具鍊 C 中。

本文件將詳細說明使用工具鍊解決常見問題的最佳做法 GN 代碼 (.gn.gni 檔案) 發生問題。這些最佳做法 以及 Fuchsia 建構系統的最佳做法 政策

詳情請參閱 GN 工具鍊和 Fuchsia Build。 工具鍊的運作方式,或執行 fx gn help toolchain 查看 GN 的內建 說明文件。

目標

本文件中採用的最佳做法是 目標:

  • 一致性:偏好其中一種方法。
  • 清楚性。使用斷言清楚傳達意圖。
  • 效能:避免建構時執行不必要的工作。

最佳做法

在預期的工具鍊中斷言

如果檔案只會用於單一工具鍊或特定工具鍊中, 把斷言放在頂部

建議使用:斷言is_host 只會建構主機執行檔的 BUILD.gn 檔案。

assert(is_host)

# ...

建議:在僅在預設工具鍊中合理使用的範本中宣告 current_toolchain == default_toolchain

template("foo") {
  assert(current_toolchain == default_toolchain,
         "The foo template can only be used in the default toolchain")
}

將目標納入條件式

如果您無法在預期內提出聲明 工具鍊,因為檔案需要使用 將目標包裝在條件區塊中,以避免不必要的 。如此一來,您就能更輕鬆瞭解在何處使用哪些目標 這也能協助減少 GN 產生時間

建議:包裝 is_host 中的目標 和 is_fuchsia 檢查

# example/BUILD.gn

executable("built_everywhere") {
  # ...
}

if (is_host) {
  executable("only_on_host") {
    # ...
  }
}

if (is_fuchsia) {
  executable("only_on_fuchsia") {
    # ...
  }
}

不建議的做法:定義所有指定目標 無條件使用

# example/BUILD.gn

executable("built_everywhere") {
  # ...
}

executable("only_on_host") {
  # ...
}

executable("only_on_fuchsia") {
  # ...
}

這種做法會增加目標數量,並減緩 GN 和 ninja 的速度。 舉例來說,當 GN 在example:only_on_fuchsia 預設工具鍊,它會評估預設工具鍊中的所有 example/BUILD.gn 工具鍊,包括 only_on_host 目標。因為這層串聯防護機制 導致錯誤的可能性 到數萬個不適用的目標

使用 is_* 變數檢查工具鍊

聲明或撰寫 目前工具鍊上的 conditional,請使用其中一個 is_*變數 BUILDCONFIG.gn (如果滿足您的需求):

is_android = false
is_chromeos = false
is_fuchsia = false
is_fuchsia_host = false
is_host = false
is_ios = false
is_linux = false
is_mac = false
is_win = false
is_component_build = false
is_official_build = false

建議採用:使用 is_host 檢查 主機工具鍊。

if (is_host) {
  # ...
}

不建議使用:使用 current_toolchain == host_toolchain 檢查主機工具鍊。

if (current_toolchain == host_toolchain) {
  # ...
}

檢查 current_toolchain == host_toolchain 通常有誤,因為檢查中 是多個主機工具鍊 (當涉及變化版本時)。

除非有理由,否則請勿檢查 current_toolchain 的值。 舉例來說,其中一種有效的用途是檢查 current_toolchain == default_toolchain 是否定義跨工具鍊 動作

偏好更少且早期的工具鍊重新導向

如要進入非預設工具鍊,您必須在某個時間點重新導向至該工具鍊。 請盡可能將這些重新導向推送到建構圖的上。這會產生 還能減少重新導向 宣告預期的工具鍊

建議使用:重新導向至 host_toolchain 在建構初期執行這項作業

# example/BUILD.gn

group("tests") {
  testonly = true
  deps = [ "foo:tests($host_toolchain)" ]
}
# examples/foo/BUILD.gn

assert(is_host)

test("foo_unit_tests") {
  # ...
}

test("foo_integration_tests") {
  # ...
}

group("tests") {
  testonly = true
  deps = [
    ":foo_unit_tests",
    ":foo_integration_tests",
  ]
}

不建議使用:重新導向至以下網域: 在建構中之後多次執行 host_toolchain

# example/BUILD.gn

group("tests") {
  testonly = true
  deps = [ "foo:tests" ]
}
# examples/foo/BUILD.gn

if (is_host) {
  test("foo_unit_tests") {
    # ...
  }

  test("foo_integration_tests") {
    # ...
  }
}

group("tests") {
  testonly = true
  deps = [
    ":foo_unit_tests($host_toolchain)",
    ":foo_integration_tests($host_toolchain)",
  ]
}

這個方法不需要重複處理範例/foo/BUILD.gn 兩次, 預設工具鍊,然後再在主機工具鍊中再次執行。

避免自動工具鍊轉送

如果目標僅在特定工具鍊中具有意義,請直接參考 預期的工具鍊

建議:在預期中斷言 並定義目標一次。

assert(current_toolchain == desired_toolchain)

action(target_name) {
  # ...
}

不建議使用:隱藏工具鍊 要求自動重新導向所有其他工具鍊的 GN 群組。

if (current_toolchain == desired_toolchain) {
  action(target_name) {
    # ...
  }
} else {
  group(target_name) {
    public_deps = [ ":$target_name($desired_toolchain)" ]
  }
}

雖然在任何工具鍊中實現目標看起來似乎很方便, 讓你更難瞭解實際情況

將無工具鍊動作放入預設工具鍊

某些操作在工具鍊上的行為都相同,因此會浪費資源 以便在多個工具鍊中重複執行這些指令最常見的例子是 :雖然我們可能會在多個工具鍊中建構結果程式碼, 不必每次都重新產生程式碼如要解決這個問題,請確認 動作只會在 default_toolchain 中定義。

建議做法:在 預設工具鍊。

if (current_toolchain == default_toolchain) {
  action("codegen") {
    visibility = [ ":*" ]
    outputs = [ "$target_gen_dir/main.cc" ]
    # ...
  }
}

executable("program") {
  deps = [ ":codegen($default_toolchain)" ]
  sources = get_target_outputs(deps[0])
  # ...
}

不建議使用:在 每個工具鍊。

action("codegen") {
  visibility = [ ":*" ]
  outputs = [ "$target_gen_dir/main.cc" ]
  # ...
}

executable("program") {
  deps = [ ":codegen" ]
  sources = get_target_outputs(deps[0])
  # ...
}

使用 :anything 標籤取得輸出目錄

使用「target_gen_dir」呼叫 get_label_info 時或「target_out_dir」,只有 像是標籤的目錄,而不是目標名稱如果沒有 顯示有意義的目標,請使用名為「任何內容」的假目標。

建議:將假目標命名為「anything」。

codegen_dir = get_label_info(":anything($default_toolchain)", "target_gen_dir")

不建議的做法:為假目標命名,而不是「任何內容」。

codegen_dir = get_label_info(":bogus($default_toolchain)", "target_gen_dir")

避免使用特定語言的工具鍊

請勿針對特定程式設計語言建立工具鍊。我們的做法 結果顯示,這真的是不好的想法。舉例來說,我們過去 rust_toolchain但後來移除了該商家檔案。我們也打算 移除 fidl_toolchain