總覽
在 GN 中,工具鍊可讓您透過多種方式建立目標。您需要瞭解 GN 程式碼並進行偵錯。由於 GN 程式碼可在 current_toolchain
上進行條件式,因此在工具鍊 A 中執行特定動作的目標,在工具鍊 B 中可能執行的是完全不同的作業,而且可能完全不會出現在工具鍊 C 中。
本文件詳細說明使用工具鍊解決 GN 程式碼 (.gn
和 .gni
檔案) 常見問題的最佳做法。除了《Fuchsia 建構系統政策》中所列的最佳做法以外,這些最佳做法也了。
請參閱 GN 工具鍊和 Fuchsia Build 進一步瞭解工具鍊的運作方式,或是執行 fx gn help toolchain
查看 GN 的內建說明文件。
目標
本文件中的最佳做法是以下列目標為基礎:
- 一致性:希望能採取一種做法。
- 清晰度。透過斷言清楚傳達意圖。
- 效能:避免建構中不必要的工作。
最佳做法
斷言預期的工具鍊
如果檔案只應用於一個工具鍊或特定工具鍊,請在頂部放置斷言。
建議:在僅建構主機執行檔的 BUILD.gn
檔案中斷言 is_host
。
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時,如果其中一個符合您的需求,請使用 BUILDCONFIG.gn 中定義的其中一個 is_*
變數:
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」或「target_out_dir」呼叫 get_label_info
時,只有標籤的目錄很重要,而非目標名稱。如果沒有合適的特定目標,請使用名為「任何目標」的假目標。
建議:將假目標命名為「任何內容」。
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
。