cquery
是 query
的變化版本,可正確處理 select()
和建構選項的效果。
方法是針對整合這些效果的 Bazel 分析階段執行結果。query
則是先在 Bazel 的載入階段結果執行,然後再評估選項。
例如:
$ cat > tree/BUILD <<EOF sh_library( name = "ash", deps = select({ ":excelsior": [":manna-ash"], ":americana": [":white-ash"], "//conditions:default": [":common-ash"], }), ) sh_library(name = "manna-ash") sh_library(name = "white-ash") sh_library(name = "common-ash") config_setting( name = "excelsior", values = {"define": "species=excelsior"}, ) config_setting( name = "americana", values = {"define": "species=americana"}, ) EOF
# Traditional query: query doesn't know which select() branch you will choose, # so it conservatively lists all of possible choices, including all used config_settings. $ bazel query "deps(//tree:ash)" --noimplicit_deps //tree:americana //tree:ash //tree:common-ash //tree:excelsior //tree:manna-ash //tree:white-ash # cquery: cquery lets you set build options at the command line and chooses # the exact dependencies that implies (and also the config_setting targets). $ bazel cquery "deps(//tree:ash)" --define species=excelsior --noimplicit_deps //tree:ash (9f87702) //tree:manna-ash (9f87702) //tree:americana (9f87702) //tree:excelsior (9f87702)
每個結果都會在建構目標時使用的設定中有一個專屬 ID (9f87702)
。
由於 cquery
會針對設定的目標圖表執行,因此由於未設定目標,因此無法獲得構件 (例如建構動作和 [test_suite](/versions/6.2.0/reference/be/general#test_suite)
規則存取權) 的深入分析資訊。如要瞭解前者,請參閱 [aquery](/versions/6.2.0/docs/aquery)
。
基本語法
簡單的 cquery
呼叫看起來會像這樣:
bazel cquery "function(//target)"
查詢運算式 "function(//target)"
包含下列內容:
function(...)
是要在目標上執行的函式。cquery
支援query
的大部分函式,以及一些新的函式。//target
是傳遞至函式的運算式。在這個範例中,運算式是簡單的目標。但查詢語言也可用來建立函式的巢狀結構。如需範例,請參閱查詢操作說明。
cquery
要求目標必須執行載入與分析階段。除非另有指定,否則 cquery
會剖析查詢運算式中列出的目標。如要查詢頂層建構目標的依附元件,請參閱 --universe_scope
。
設定
程式碼行:
//tree:ash (9f87702)
表示 //tree:ash
已內建 ID 為 9f87702
的設定。對於大部分的目標而言,這是定義設定的建構選項值的不透明雜湊。
如要查看設定的完整內容,請執行:
$ bazel config 9f87702
主機設定會使用特殊 ID (HOST)
。非產生的來源檔案 (例如 srcs
中常見的檔案) 會使用特殊 ID (null)
(因為這類檔案不需要設定)。
9f87702
是完整 ID 的前置字串。這是因為完整 ID 是 SHA-256 雜湊,長度很長且難以追蹤。cquery
會解讀完整 ID 的任何有效前置字元,類似於 Git 短雜湊。如要查看完整 ID,請執行 $ bazel config
。
目標模式評估
//foo
對 cquery
和 query
具有不同的意義。這是因為 cquery
會評估「設定」目標,而建構圖可能有多個 //foo
的版本。
如果是 cquery
,查詢運算式中的目標模式會評估每個具備該模式標籤且已設定的目標。輸出具有確定性,但 cquery
在核心查詢排序合約之外提供任何排序保證。
相較於使用 query
,這會產生子運算式結果。例如,以下函式可能產生多個結果:
# Analyzes //foo in the target configuration, but also analyzes # //genrule_with_foo_as_tool which depends on a host-configured # //foo. So there are two configured target instances of //foo in # the build graph. $ bazel cquery //foo --universe_scope=//foo,//genrule_with_foo_as_tool //foo (9f87702) //foo (HOST)
如要精確宣告要查詢哪個執行個體,請使用 config
函式。
如要進一步瞭解目標模式,請參閱 query
的目標模式說明文件。
函式
在 query
支援的函式組合中,cquery
支援 visible
、siblings
、buildfiles
和 tests
以外的所有標記。
cquery
也引進下列新函式:
config
expr ::= config(expr, word)
config
運算子會嘗試尋找以第一個引數代表的標籤和第二個引數指定的設定目標。
第二個引數的有效值為 target
、host
、null
或自訂設定雜湊。您可以從 $
bazel config
或 cquery
的輸出內容中擷取雜湊。
示例:
$ bazel cquery "config(//bar, host)" --universe_scope=//foo
$ bazel cquery "deps(//foo)" //bar (HOST) //baz (3732cc8) $ bazel cquery "config(//baz, 3732cc8)"
如果無法在指定的設定中找到第一個引數的所有結果,系統只會傳回找到的結果。如果在指定的設定中找不到任何結果,查詢就會失敗。
選項
建構選項
cquery
會透過一般 Bazel 建構作業執行,因此會繼承建構期間可用的一組選項。
使用 cquery 選項
--universe_scope
(逗號分隔清單)
設定目標的依附元件通常會進行轉換,這會導致設定與自身的依附元件不同。這個標記可讓您查詢目標,如同建構為依附元件,或做為其他目標的遞移依附元件一樣。例如:
# x/BUILD genrule( name = "my_gen", srcs = ["x.in"], outs = ["x.cc"], cmd = "$(locations :tool) $< >$@", tools = [":tool"], ) cc_library( name = "tool", )
Genrules 在主機設定中設定其工具,因此下列查詢會產生下列輸出內容:
查詢 | 已建立的目標 | 輸出內容 |
---|---|---|
bazel cquery "//x:tool" | //x:tool | //x:tool(targetconfig) |
bazel cquery "//x:tool" --universe_scope×x:my_gen" | //x:my_gen | //x:tool(hostconfig) |
如果設定此旗標,則會建構其內容。如未設定,系統會改為建構查詢運算式中提及的所有目標。已建構目標的轉換關閉會做為查詢的宇宙。無論採用哪種方式,您建構的目標都必須在頂層建構 (也就是與頂層選項相容)。cquery
會傳回這些頂層目標的轉換關閉結果。
即使可以在頂層查詢運算式中建構所有目標,這種做法可能還是有所助益。例如,明確設定 --universe_scope
可能會防止在不重視的設定中多次建構目標。這樣做也可以協助指定要尋找的目標設定版本 (因為目前無法以任何其他方式完整指定)。如果您的查詢運算式比 deps(//foo)
複雜,則應設定此標記。
--implicit_deps
(布林值,default=True)
將此標記設為 false,會排除所有未在 BUILD 檔案中明確設定,並改由 Bazel 設定的其他結果。其中包括篩選已解析的工具鍊。
--tool_deps
(布林值,default=True)
如果將此標記設為 False,系統會排除所有已設定的目標,其路徑從查詢的目標到這些目標的路徑,會在目標設定與非目標設定之間經過轉換。如果查詢的目標位於目標設定中,設定 --notool_deps
就只會傳回也在目標設定中的目標。如果查詢的目標位於非目標設定中,設定 --notool_deps
時只會傳回非目標設定中的目標。這項設定通常不會影響已解析的工具鍊篩選。
--include_aspects
(布林值,default=True)
切面可以為建構新增其他依附元件。根據預設,cquery
不會遵循某些面向,因為可查詢的圖表會放大,因而使用更多記憶體。但遵循這些追蹤可產生更準確的結果
如果您擔心大型查詢的記憶體影響,請在 bazelrc 中預設啟用此旗標。
如果查詢已停用切面功能,目標 X 會在建構目標 Y 時失敗,但 cquery somepath(Y, X)
和 cquery deps(Y) | grep 'X'
不會因為依附元件發生在某個方面而傳回任何結果。
輸出格式
根據預設,cquery 輸出會依依附元件的順序列出標籤和設定組合。還有其他方式會顯示結果。
轉場
--transitions=lite --transitions=full
設定轉換可用於在頂層目標之下,於頂層目標之下建構目標。
舉例來說,目標可能會強制要求 tools
屬性中所有依附元件的主機設定。這就是所謂的屬性轉換。規則也可以在各自的設定中強制執行轉換,也稱為規則類別轉換。這個輸出格式會輸出這些轉換的相關資訊,例如這些轉換的類型以及對建構選項的影響。
此輸出格式由 --transitions
標記觸發,該標記預設為 NONE
。可以設為 FULL
或 LITE
模式。FULL
模式會輸出規則類別轉換和屬性轉換的相關資訊,包括轉換前後選項的詳細差異。LITE
模式會輸出相同的資訊,但不會有選項差異。
通訊協定訊息輸出內容
--output=proto
此選項會使產生的目標以二進位通訊協定緩衝區格式輸出。您可在 src/main/protobuf/analysis.proto 中找到通訊協定緩衝區的定義。
CqueryResult
是含有 cquery 結果的頂層訊息。其中包含 ConfiguredTarget
訊息的清單和 Configuration
訊息清單。每個 ConfiguredTarget
都有 configuration_id
,其值等於對應 Configuration
訊息的 id
欄位。
--[no]proto:include_configurations
根據預設,cquery 結果會傳回設定資訊,做為每個設定目標的一部分。如要省略這項資訊,並取得格式與查詢 proto 輸出內容完全相同的 proto 輸出內容,請將這個標記設為 false。
如需更多 proto 輸出相關選項,請參閱查詢的 proto 輸出說明文件。
圖表輸出內容
--output=graph
這個選項會以與 Graphviz 相容的 .dot 檔案產生輸出內容。詳情請參閱 query
的圖表輸出說明文件。cquery
也支援 --graph:node_limit
和 --graph:factored
。
檔案輸出
--output=files
這個選項會列印符合查詢的每個目標產生的輸出檔案清單,類似 bazel build
叫用結束時顯示的清單。輸出結果只會包含要求輸出群組中通告的檔案 (由 --output_groups
旗標決定)。當中包含來源檔案。
使用 Starlark 定義輸出格式
--output=starlark
這個輸出格式會針對查詢結果中的每個已設定目標呼叫 Starlark 函式,並輸出呼叫傳回的值。--starlark:file
標記會指定 Starlark 檔案的位置,該檔案定義了名為 format
的函式,且含有單一參數 target
。系統會針對查詢結果中的每個 Target 呼叫此函式。或者,為了方便起見,您也可以使用 --starlark:expr
標記,只指定宣告為 def format(target): return expr
的函式主體。
「cquery」 Starlark 方言
cquery Starlark 環境與 BUILD 或 .bzl 檔案不同。其中包含所有核心 Starlark 內建常數和函式,以及下方所述的幾項特定 C 查詢查詢,但不支援 glob
、native
或 rule
,且不支援載入陳述式。
build_options(target)
build_options(target)
會傳回一個對應,其鍵為建構選項 ID (請參閱設定),且其值是其 Starlark 值。這張地圖會省略其值不是合法 Starlark 值的建構選項。
如果指定目標是輸入檔案,由於輸入檔案目標的設定為空值,build_options(target)
會傳回 None。
供應商(目標)
providers(target)
會傳回對應,其鍵為提供者名稱 (例如 "DefaultInfo"
),其值是其 Starlark 值。如果該供應商的值不是法定 Starlark 值,則這張地圖會省略。
範例
列印 //foo
產生的所有檔案基礎名稱清單 (以空格分隔):
bazel cquery //foo --output=starlark \ --starlark:expr="' '.join([f.basename for f in target.files.to_list()])"
列印在 //bar
及其子套件中由 rule 目標產生的所有檔案路徑,並以空格分隔的清單:
bazel cquery 'kind(rule, //bar/...)' --output=starlark \ --starlark:expr="' '.join([f.path for f in target.files.to_list()])"
列印由 //foo
註冊的所有動作記憶拼音清單。
bazel cquery //foo --output=starlark \ --starlark:expr="[a.mnemonic for a in target.actions]"
列印由 cc_library
//baz
註冊的編譯輸出清單。
bazel cquery //baz --output=starlark \ --starlark:expr="[f.path for f in target.output_groups.compilation_outputs.to_list()]"
建構 //foo
時,列印指令列選項 --javacopt
的值。
bazel cquery //foo --output=starlark \ --starlark:expr="build_options(target)['//command_line_option:javacopt']"
輸出每個目標的標籤只能有一個輸出內容。這個範例使用檔案中定義的 Starlark 函式。
$ cat example.cquery def has_one_output(target): return len(target.files.to_list()) == 1 def format(target): if has_one_output(target): return target.label else: return "" $ bazel cquery //baz --output=starlark --starlark:file=example.cquery
列印每個目標 (嚴格 Python 3) 的標籤。這個範例使用檔案中定義的 Starlark 函式。
$ cat example.cquery def format(target): p = providers(target) py_info = p.get("PyInfo") if py_info and py_info.has_py3_only_sources: return target.label else: return "" $ bazel cquery //baz --output=starlark --starlark:file=example.cquery
從使用者定義的供應器擷取值。
$ cat some_package/my_rule.bzl MyRuleInfo = provider(fields={"color": "the name of a color"}) def _my_rule_impl(ctx): ... return [MyRuleInfo(color="red")] my_rule = rule( implementation = _my_rule_impl, attrs = {...}, ) $ cat example.cquery def format(target): p = providers(target) my_rule_info = p.get("//some_package:my_rule.bzl%MyRuleInfo'") if my_rule_info: return my_rule_info.color return "" $ bazel cquery //baz --output=starlark --starlark:file=example.cquery
cquery 與查詢
cquery
和 query
可以相輔相成,展現卓越的專業能力。請考量下列要點,找出最適合您的做法:
cquery
遵循特定的select()
分支版本,以便建立您建構的確切圖表。query
不知道建構作業選擇哪一個分支,因此會納入所有分支版本,進而造成過於粗略。cquery
的精確度需要建構比query
更多的圖表。具體來說,cquery
會評估設定的目標,而query
只會評估目標。這會比較耗時,且會耗用較多記憶體。cquery
對查詢語言的解釋功能會產生query
避免的混淆。舉例來說,如果"//foo"
同時存在於兩個設定中,cquery "deps(//foo)"
應使用哪一項設定?[config](#config)
函式可協助您進行這項操作。cquery
目前是新的工具,並不支援特定用途。詳情請參閱已知問題。
已知問題
cquery
「版本」的所有目標都必須採用相同設定。
評估查詢之前,cquery
會先觸發建構作業,直到執行建構動作為止。系統預設會從查詢運算式中顯示的所有標籤選取「建構」目標 (可以使用 --universe_scope
覆寫)。這些目標的設定必須相同。
雖然這些規則通常會共用頂層「目標」設定,但規則可以透過傳入的邊緣轉換自行變更設定。這是 cquery
短處。
解決方法:如果可以,請將 --universe_scope
設為較嚴格的範圍。例如:
# This command attempts to build the transitive closures of both //foo and # //bar. //bar uses an incoming edge transition to change its --cpu flag. $ bazel cquery 'somepath(//foo, //bar)' ERROR: Error doing post analysis query: Top-level targets //foo and //bar have different configurations (top-level targets with different configurations is not supported) # This command only builds the transitive closure of //foo, under which # //bar should exist in the correct configuration. $ bazel cquery 'somepath(//foo, //bar)' --universe_scope=//foo
不支援 --output=xml
。
非確定性的輸出內容。
cquery
不會自動從先前的指令清除建構圖表,因此很容易從過去查詢中取得結果。例如,genquery
會在其 tools
屬性上揭露主機轉換,也就是說,會在主機設定中設定其工具。
您可以在下方看到轉換效果的連帶效果。
$ cat > foo/BUILD <<<EOF genrule( name = "my_gen", srcs = ["x.in"], outs = ["x.cc"], cmd = "$(locations :tool) $< >$@", tools = [":tool"], ) cc_library( name = "tool", ) EOF $ bazel cquery "//foo:tool" tool(target_config) $ bazel cquery "deps(//foo:my_gen)" my_gen (target_config) tool (host_config) ... $ bazel cquery "//foo:tool" tool(host_config)
解決方法:變更任何啟動選項,強制重新分析已設定的目標。例如,將 --test_arg=<whatever>
加入建構指令。
疑難排解
遞迴目標模式 (/...
)
如果看到:
$ bazel cquery --universe_scope=//foo:app "somepath(//foo:app, //foo/...)" ERROR: Error doing post analysis query: Evaluation failed: Unable to load package '[foo]' because package is not in scope. Check that all target patterns in query expression are within the --universe_scope of this query.
即使 --universe_scope=//foo:app
包含 //foo
套件,但系統卻誤以為該套件不在範圍內。這是因為 cquery
的設計限制。為解決這個問題,請明確將 //foo/...
納入宇宙範圍:
$ bazel cquery --universe_scope=//foo:app,//foo/... "somepath(//foo:app, //foo/...)"
如果這個方法無效 (例如 //foo/...
中的某些目標無法以所選建構旗標進行建構),請使用預先處理查詢,將該模式手動解除包裝至其父項套件:
# Replace "//foo/..." with a subshell query call (not cquery!) outputting each package, piped into # a sed call converting "<pkg>" to "//<pkg>:*", piped into a "+"-delimited line merge. # Output looks like "//foo:*+//foo/bar:*+//foo/baz". # $ bazel cquery --universe_scope=//foo:app "somepath(//foo:app, $(bazel query //foo/... --output=package | sed -e 's/^/\/\//' -e 's/$/:*/' | paste -sd "+" -))"