您可以使用 aquery 指令查詢建構圖中的動作。
這項工具會根據分析後的設定目標圖表運作,並顯示動作、構件及其關係的相關資訊。
如果您對從設定的目標圖表產生的動作/構件屬性感興趣,aquery 就很有用。例如,實際執行的指令及其輸入/輸出/助記符。
這項工具可接受多個指令列選項。 值得注意的是,aquery 指令會在一般 Bazel 建構作業上執行,並繼承建構期間可用的選項集。
它支援與傳統 query 相同的函式集,但 siblings、buildfiles 和 tests 除外。
以下是 aquery 的輸出內容範例 (不含特定詳細資料):
$ bazel aquery 'deps(//some:label)' action 'Writing file some_file_name' Mnemonic: ... Target: ... Configuration: ... ActionKey: ... Inputs: [...] Outputs: [...]
基本語法
aquery 的簡單語法範例如下:
bazel aquery "aquery_function(function(//target))"
查詢運算式 (以半形引號括住) 包含下列項目:
# aquery examples:
# Get the action graph generated while building //src/target_a
$ bazel aquery '//src/target_a'
# Get the action graph generated while building all dependencies of //src/target_a
$ bazel aquery 'deps(//src/target_a)'
# Get the action graph generated while building all dependencies of //src/target_a
# whose inputs filenames match the regex ".*cpp".
$ bazel aquery 'inputs(".*cpp", deps(//src/target_a))'
使用查詢函式
共有三種 aquery 函式:
inputs:依輸入內容篩選動作。outputs:依輸出內容篩選動作mnemonic:依助記符篩選動作
expr ::= inputs(word, expr)
inputs 運算子會傳回從建構 expr 產生的動作,這些動作的輸入檔案名稱符合 word 提供的規則運算式。
$ bazel aquery 'inputs(".*cpp", deps(//src/target_a))'
outputs 和 mnemonic 函式採用類似的語法。
您也可以合併函式來執行 AND 運算。例如:
$ bazel aquery 'mnemonic("Cpp.*", (inputs(".*cpp", inputs("foo.*", //src/target_a))))'
上述指令會找出所有參與建構 //src/target_a 的動作,這些動作的助記符與 "Cpp.*" 相符,輸入內容則與 ".*cpp" 和 "foo.*" 模式相符。
產生的語法錯誤範例如下:
$ bazel aquery 'deps(inputs(".*cpp", //src/target_a))'
ERROR: aquery filter functions (inputs, outputs, mnemonic) produce actions,
and therefore can't be the input of other function types: deps
deps(inputs(".*cpp", //src/target_a))
選項
建構選項
aquery 會在一般 Bazel 建構作業上執行,因此會沿用建構期間可用的選項集。
Aquery 選項
--output=(text|summary|proto|jsonproto|textproto), default=text
預設輸出格式 (text) 為人類可讀的格式,如要使用機器可讀的格式,請使用 proto、textproto 或 jsonproto。
Proto 訊息為 analysis.ActionGraphContainer。
--include_commandline, default=true
輸出內容中包含動作指令列的內容 (可能很大)。
--include_artifacts, default=true
輸出內容會包含動作輸入和輸出的名稱 (可能很大)。
--include_aspects, default=true
是否要在輸出內容中加入 Aspect 產生的動作。
--include_param_files, default=false
加入指令中使用的參數檔案內容 (可能很大)。
--include_file_write_contents, default=false
包含 actions.write() 動作的檔案內容,以及 SourceSymlinkManifest 動作的資訊清單檔案內容。檔案內容會以 --output=xxxproto 形式傳回 file_contents 欄位。使用 --output=text 時,輸出內容會包含
FileWriteContents: [<base64-encoded file contents>]
行
--skyframe_state, default=false
從 Skyframe 傾印 Action Graph,不必執行額外分析。
其他工具和功能
根據 Skyframe 的狀態查詢
Skyframe 是 Bazel 的評估和遞增模型。在每個 Bazel 伺服器執行個體上,Skyframe 會儲存從先前分析階段建構的依附元件圖表。
在某些情況下,查詢 Skyframe 上的動作圖表會很有幫助。舉例來說:
- 執行
bazel build //target_a - 執行
bazel build //target_b - 已產生檔案「
foo.out」。
身為 Bazel 使用者,我想判斷 foo.out 是從建構 //target_a 還是 //target_b 產生。
您可以執行 bazel aquery 'outputs("foo.out", //target_a)' 和 bazel aquery 'outputs("foo.out", //target_b)',找出負責建立 foo.out 的動作,進而找出目標。不過,先前建構的不同目標數量可能超過 2 個,因此執行多個 aquery 指令會很麻煩。
或者,您也可以使用 --skyframe_state 旗標:
# List all actions on Skyframe's action graph
$ bazel aquery --output=proto --skyframe_state
# or
# List all actions on Skyframe's action graph, whose output matches "foo.out"
$ bazel aquery --output=proto --skyframe_state 'outputs("foo.out")'
使用 --skyframe_state 模式時,aquery 會擷取 Skyframe 保存在 Bazel 執行個體上的動作圖表內容,(選擇性) 對其執行篩選,然後輸出內容,而不會重新執行分析階段。--skyframe_stateaquery
特殊注意事項
輸出格式
--skyframe_state 目前僅適用於 --output=proto 和 --output=textproto
查詢運算式中未納入目標標籤
目前,--skyframe_state 會查詢 Skyframe 上存在的所有動作圖表,無論目標為何。在查詢中指定目標標籤,並搭配 --skyframe_state,會視為語法錯誤:
# WRONG: Target Included
$ bazel aquery --output=proto --skyframe_state **//target_a**
ERROR: Error while parsing '//target_a)': Specifying build target(s) [//target_a] with --skyframe_state is currently not supported.
# WRONG: Target Included
$ bazel aquery --output=proto --skyframe_state 'inputs(".*.java", **//target_a**)'
ERROR: Error while parsing '//target_a)': Specifying build target(s) [//target_a] with --skyframe_state is currently not supported.
# CORRECT: Without Target
$ bazel aquery --output=proto --skyframe_state
$ bazel aquery --output=proto --skyframe_state 'inputs(".*.java")'
比較 aquery 輸出內容
您可以使用 aquery_differ 工具,比較兩次不同 aquery 呼叫的輸出內容。舉例來說,當您對規則定義進行一些變更,並想確認執行的指令列是否未變更時,aquery_differ 是這方面的工具。
這項工具位於 bazelbuild/bazel 存放區。如要使用這項工具,請將存放區複製到本機電腦。使用範例:
$ bazel run //tools/aquery_differ -- \ --before=/path/to/before.proto \ --after=/path/to/after.proto \ --input_type=proto \ --attrs=cmdline \ --attrs=inputs
上述指令會傳回 before 和 after aquery 輸出內容之間的差異:哪些動作出現在其中一個輸出內容,但未出現在另一個輸出內容;哪些動作在每個 aquery 輸出內容中都有不同的指令列/輸入內容等。執行上述指令的結果如下:
Aquery output 'after' change contains an action that generates the following outputs that aquery output 'before' change doesn't:
...
/list of output files/
...
[cmdline]
Difference in the action that generates the following output(s):
/path/to/abc.out
--- /path/to/before.proto
+++ /path/to/after.proto
@@ -1,3 +1,3 @@
...
/cmdline diff, in unified diff format/
...
指令選項
--before, --after:要比較的 aquery 輸出檔案
--input_type=(proto|text_proto), default=proto:輸入檔案的格式。支援 proto 和 textproto aquery 輸出。
--attrs=(cmdline|inputs), default=cmdline:要比較的動作屬性。
切面與切面
Aspects 可以彼此疊加。這些 Aspect 產生的動作的 aquery 輸出內容,隨後會包含「Aspect 路徑」,也就是套用至目標的 Aspect 序列,該目標會產生動作。
Aspect-on-Aspect 範例:
t0 ^ | <- a1 t1 ^ | <- a2 t2
假設 ti 是規則 ri 的目標,該規則會將 Aspect ai 套用至其依附元件。
假設 a2 套用至目標 t0 時會產生動作 X。動作 X 的 bazel aquery --include_aspects 'deps(//t2)' 文字輸出內容如下:
action ...
Mnemonic: ...
Target: //my_pkg:t0
Configuration: ...
AspectDescriptors: [//my_pkg:rule.bzl%**a2**(foo=...)
-> //my_pkg:rule.bzl%**a1**(bar=...)]
...
這表示動作 X 是由套用至 a1(t0) 的 Aspect a2 所產生,而 a1(t0) 是套用至目標 t0 的 Aspect a1 結果。
每個 AspectDescriptor 的格式如下:
AspectClass([param=value,...])
AspectClass 可以是 Aspect 類別的名稱 (適用於原生 Aspect),也可以是 bzl_file%aspect_name (適用於 Starlark Aspect)。AspectDescriptor 會依依附元件圖表的拓撲順序排序。
連結至 JSON 設定檔
查詢會提供建構中執行的動作相關資訊 (執行原因、輸入/輸出),而 JSON 設定檔則會顯示動作的執行時間和持續時間。您可以透過共同分母 (動作的主要輸出內容),合併這 2 組資訊。
如要在 JSON 設定檔中加入動作的輸出內容,請使用 --experimental_include_primary_output --noslim_profile 產生設定檔。精簡設定檔無法納入主要輸出內容。查詢預設會納入動作的主要輸出內容。
我們目前沒有提供標準工具來合併這 2 個資料來源,但您應該可以運用上述資訊,自行建構指令碼。
已知問題
處理共用動作
有時,系統會共用已設定目標之間的動作。
在執行階段,這些共用動作只會視為一個動作,且只會執行一次。不過,aquery 是在執行前和分析後動作圖上運作,因此會將這些視為個別動作,且輸出構件具有完全相同的 execPath。因此,等效構件會重複顯示。
如要查看 aquery 問題/預計推出的功能清單,請前往 GitHub。
常見問題
即使輸入檔案的內容有所變更,ActionKey 仍會維持不變。
在查詢的脈絡中,ActionKey 是指從 ActionAnalysisMetadata#getKey 取得的 String:
Returns a string encoding all of the significant behaviour of this Action that might affect the
output. The general contract of `getKey` is this: if the work to be performed by the
execution of this action changes, the key must change.
...
Examples of changes that should affect the key are:
- Changes to the BUILD file that materially affect the rule which gave rise to this Action.
- Changes to the command-line options, environment, or other global configuration resources
which affect the behaviour of this kind of Action (other than changes to the names of the
input/output files, which are handled externally).
- An upgrade to the build tools which changes the program logic of this kind of Action
(typically this is achieved by incorporating a UUID into the key, which is changed each
time the program logic of this action changes).
Note the following exception: for actions that discover inputs, the key must change if any
input names change or else action validation may falsely validate.
這不包括輸入檔案內容的變更,也不要與 RemoteCacheClient#ActionKey 混淆。
更新
如有任何問題/功能要求,請按這裡回報問題。