本教學課程說明如何使用 Bazel,使用預先建立的 Bazel 專案追蹤程式碼中的依附元件。
如要進一步瞭解語言和 --output
標記,請參閱 Bazel 查詢參考資料和 Bazel cquery 參考資料手冊。在 IDE 中輸入 bazel help query
或 bazel help cquery
,即可在 IDE 中取得說明。
目標
本指南會逐一說明一組基本查詢,可用來進一步瞭解專案檔案依附元件。此內容主要協助具備 Bazel 和 BUILD
檔案運作方式基本知識的新 Bazel 開發人員。
必要條件
如果您尚未安裝 Bazel,請先安裝。本教學課程使用 Git 控管原始碼,因此為了獲得最佳結果,請一併安裝 Git。
如要將依附元件圖表製作成圖表,請使用 Graphviz 工具下載,跟上圖一樣。
取得範例專案
接著,在您選擇的指令列工具中執行下列指令,以從 Bazel 的 Example 存放區擷取範例應用程式:
git clone https://github.com/bazelbuild/examples.git
本教學課程的範例專案位於 examples/query-quickstart
目錄中。
開始使用
什麼是 Bazel 查詢?
查詢可分析 BUILD
檔案之間的關聯,並檢查產生的輸出結果,藉此提供實用資訊,協助您瞭解 Bazel 程式碼集。本指南將說明部分基本查詢函式,如需更多選項,請參閱查詢指南。查詢可協助您瞭解大型專案的依附元件,而不必手動瀏覽 BUILD
檔案。
如要執行查詢,請開啟指令列終端機並輸入:
bazel query 'query_function'
情境
試想情境:將 Cafe Bazel 與相關廚師之間的關係深入探討。這間咖啡廳專賣披薩、馬卡和起司請參考以下專案的架構:
bazelqueryguide
├── BUILD
├── src
│ └── main
│ └── java
│ └── com
│ └── example
│ ├── customers
│ │ ├── Jenny.java
│ │ ├── Amir.java
│ │ └── BUILD
│ ├── dishes
│ │ ├── Pizza.java
│ │ ├── MacAndCheese.java
│ │ └── BUILD
│ ├── ingredients
│ │ ├── Cheese.java
│ │ ├── Tomatoes.java
│ │ ├── Dough.java
│ │ ├── Macaroni.java
│ │ └── BUILD
│ ├── restaurant
│ │ ├── Cafe.java
│ │ ├── Chef.java
│ │ └── BUILD
│ ├── reviews
│ │ ├── Review.java
│ │ └── BUILD
│ └── Runner.java
└── MODULE.bazel
除非另有指示,否則在整個教學課程中,請盡量不要查看 BUILD
檔案來尋找所需資訊,而是改為單獨使用查詢函式。
一個專案包含咖啡廳的各種套件。分隔為:restaurant
、ingredients
、dishes
、customers
和 reviews
。這些套件中的規則使用各種標記和依附元件,定義 Cafe 的各種元件。
執行建構
這項專案在 Runner.java
內包含您可以執行的主要方法
輸出咖啡店菜單透過指令使用 Bazel 建構專案
bazel build
,並使用 :
表示目標名稱為 runner
。詳情請見
目標名稱:
參照目標。
如要建構這項專案,請將這個指令貼到終端機:
bazel build :runner
如果建構成功,輸出內容應如下所示。
INFO: Analyzed target //:runner (49 packages loaded, 784 targets configured).
INFO: Found 1 target...
Target //:runner up-to-date:
bazel-bin/runner.jar
bazel-bin/runner
INFO: Elapsed time: 16.593s, Critical Path: 4.32s
INFO: 23 processes: 4 internal, 10 darwin-sandbox, 9 worker.
INFO: Build completed successfully, 23 total actions
成功建構完成後,請貼上以下指令來執行應用程式:
bazel-bin/runner
--------------------- MENU -------------------------
Pizza - Cheesy Delicious Goodness
Macaroni & Cheese - Kid-approved Dinner
----------------------------------------------------
這可讓您取得提供的選單項目清單和簡短說明。
探索目標
在本專案中,系統會建立一個食材和料理項目,並將食材列出。如要透過查詢查看套件的規則,請執行 bazel query package/…
指令
在這個案例中,你可以按照下列步驟瀏覽這間咖啡廳的食材和料理:
bazel query //src/main/java/com/example/dishes/...
bazel query //src/main/java/com/example/ingredients/...
如要查詢食材套件的目標,輸出內容應如下所示:
//src/main/java/com/example/ingredients:cheese
//src/main/java/com/example/ingredients:dough
//src/main/java/com/example/ingredients:macaroni
//src/main/java/com/example/ingredients:tomato
尋找依附元件
跑者在跑步時依靠哪些目標?
假設您想要深入瞭解專案結構,但不想依賴檔案系統 (對大型專案而言可能不行)。Cafe Bazel 會使用哪些規則?
如本範例所示,執行器的目標為 runner
,請執行下列指令,探索目標的基礎依附元件:
bazel query --noimplicit_deps "deps(target)"
bazel query --noimplicit_deps "deps(:runner)"
//:runner
//:src/main/java/com/example/Runner.java
//src/main/java/com/example/dishes:MacAndCheese.java
//src/main/java/com/example/dishes:Pizza.java
//src/main/java/com/example/dishes:macAndCheese
//src/main/java/com/example/dishes:pizza
//src/main/java/com/example/ingredients:Cheese.java
//src/main/java/com/example/ingredients:Dough.java
//src/main/java/com/example/ingredients:Macaroni.java
//src/main/java/com/example/ingredients:Tomato.java
//src/main/java/com/example/ingredients:cheese
//src/main/java/com/example/ingredients:dough
//src/main/java/com/example/ingredients:macaroni
//src/main/java/com/example/ingredients:tomato
//src/main/java/com/example/restaurant:Cafe.java
//src/main/java/com/example/restaurant:Chef.java
//src/main/java/com/example/restaurant:cafe
//src/main/java/com/example/restaurant:chef
在多數情況下,您可以使用查詢函式 deps()
查看特定目標的個別輸出依附元件。
以視覺化方式呈現依附元件圖表 (選用)
本節說明如何以視覺化方式呈現特定查詢的依附元件路徑。Graphviz 可讓你查看路徑為有向非循環圖的圖片,而非扁平化清單。您可以使用各種 --output
指令列選項,改變 Bazel 查詢圖形的顯示方式。請參閱輸出格式瞭解選項。
請先執行所需的查詢,並新增 --noimplicit_deps
旗標,移除過多的工具依附元件。然後,依照輸出旗標的查詢操作,將圖表儲存至名為 graph.in
的檔案,以以文字方式呈現圖表。
如何搜尋目標 :runner
的所有依附元件,並將輸出內容的格式設為圖表:
bazel query --noimplicit_deps 'deps(:runner)' --output graph > graph.in
這會建立名為 graph.in
的檔案,該檔案是建構圖表的文字表示。Graphviz 使用 dot
將文字處理成圖表,用來建立 png:
dot -Tpng < graph.in > graph.png
開啟 graph.png
時,畫面上應如下所示。下圖經過簡化,使本指南中的重要路徑詳細資訊更加清楚。
當您想查看本指南中不同查詢函式的輸出內容時,這項功能就很有用。
尋找反向依附元件
相反地,如果您只是想分析其他目標使用該目標,可以透過查詢找出依特定規則依附的目標。這就是所謂的「反向依附元件」。在不熟悉的程式碼集中編輯檔案時,使用 rdeps()
不僅是實用的做法,還可以避免在不知情的情況下破壞其他依附的檔案。
舉例來說,你想要編輯食材 cheese
。為避免造成 Cafe Bazel 發生問題,請檢查哪些餐點仰賴 cheese
。
如要查看依附特定目標/套件的目標,您可以使用 rdeps(universe_scope, target)
。rdeps()
查詢函式至少使用兩個引數:universe_scope
(相關目錄) 和 target
。Bazel 會在提供的 universe_scope
中搜尋目標的反向依附元件。rdeps()
運算子接受選用的第三個引數:這個整數常值會指定搜尋深度的上限。
如要在整個專案「//...」的範圍內尋找目標 cheese
的反向依附元件,請執行下列指令:
bazel query "rdeps(universe_scope, target)"
ex) bazel query "rdeps(//... , //src/main/java/com/example/ingredients:cheese)"
//:runner
//src/main/java/com/example/dishes:macAndCheese
//src/main/java/com/example/dishes:pizza
//src/main/java/com/example/ingredients:cheese
//src/main/java/com/example/restaurant:cafe
//src/main/java/com/example/restaurant:chef
查詢傳回顯示,起司都依賴披薩和 macAndCheese。真驚喜!
根據標記尋找目標
有兩位客戶走進 Bazel Cafe:Amir 和 Jenny。他們一無所知,只有其名字才知道。值得慶幸的是,他們的訂單已在「客戶」中標記BUILD
檔案。如何存取這個代碼?
開發人員可以使用不同 ID 標記 Bazel 目標,通常用於測試。舉例來說,測試中的標記可以在偵錯和發布程序中為測試的角色加上註解,尤其是針對缺少執行階段註解功能的 C++ 和 Python 測試。使用標記和大小元素可讓您根據程式碼集的檢查政策,靈活組合測試套件。
在本例中,標記是 pizza
或 macAndCheese
之一,代表選單項目。這個指令會查詢特定套件內含有與您 ID 相符的標記的目標。
bazel query 'attr(tags, "pizza", //src/main/java/com/example/customers/...)'
這個查詢會傳回「customers」且帶有「披薩」標記的包裹
測試自己
使用這個查詢,瞭解美珍想要點什麼。
解答
Mac 和起司
新增依附元件
Bazel 的 Bazel 已經擴大菜單,顧客現在可以點餐了!這種冰沙包括成分 Strawberry
和 Banana
。
首先,請新增平滑化的成分:Strawberry.java
和 Banana.java
。新增空白的 Java 類別。
src/main/java/com/example/ingredients/Strawberry.java
package com.example.ingredients;
public class Strawberry {
}
src/main/java/com/example/ingredients/Banana.java
package com.example.ingredients;
public class Banana {
}
接著,將 Smoothie.java
新增至適當的目錄:dishes
。
src/main/java/com/example/dishes/Smoothie.java
package com.example.dishes;
public class Smoothie {
public static final String DISH_NAME = "Smoothie";
public static final String DESCRIPTION = "Yummy and Refreshing";
}
最後,在適當的 BUILD
檔案中將這些檔案新增為規則。為每項新食材建立新的 Java 程式庫,包括其名稱、公開瀏覽權限以及新建立的「src」檔案。您應該會取得這個更新後的 BUILD
檔案:
src/main/java/com/example/ingredients/BUILD
java_library(
name = "cheese",
visibility = ["//visibility:public"],
srcs = ["Cheese.java"],
)
java_library(
name = "dough",
visibility = ["//visibility:public"],
srcs = ["Dough.java"],
)
java_library(
name = "macaroni",
visibility = ["//visibility:public"],
srcs = ["Macaroni.java"],
)
java_library(
name = "tomato",
visibility = ["//visibility:public"],
srcs = ["Tomato.java"],
)
java_library(
name = "strawberry",
visibility = ["//visibility:public"],
srcs = ["Strawberry.java"],
)
java_library(
name = "banana",
visibility = ["//visibility:public"],
srcs = ["Banana.java"],
)
在餐點的 BUILD
檔案中,你想新增 Smoothie
規則。這樣做包括為 Smoothie
建立的 Java 檔案做為「src」以及您為每個冰沙食材做出的新規則。
src/main/java/com/example/dishes/BUILD
java_library(
name = "macAndCheese",
visibility = ["//visibility:public"],
srcs = ["MacAndCheese.java"],
deps = [
"//src/main/java/com/example/ingredients:cheese",
"//src/main/java/com/example/ingredients:macaroni",
],
)
java_library(
name = "pizza",
visibility = ["//visibility:public"],
srcs = ["Pizza.java"],
deps = [
"//src/main/java/com/example/ingredients:cheese",
"//src/main/java/com/example/ingredients:dough",
"//src/main/java/com/example/ingredients:tomato",
],
)
java_library(
name = "smoothie",
visibility = ["//visibility:public"],
srcs = ["Smoothie.java"],
deps = [
"//src/main/java/com/example/ingredients:strawberry",
"//src/main/java/com/example/ingredients:banana",
],
)
最後,您想在 Chef 的 BUILD
檔案中加入冰沙做為依附元件。
src/main/java/com/example/restaurant/BUILD
java\_library(
name = "chef",
visibility = ["//visibility:public"],
srcs = [
"Chef.java",
],
deps = [
"//src/main/java/com/example/dishes:macAndCheese",
"//src/main/java/com/example/dishes:pizza",
"//src/main/java/com/example/dishes:smoothie",
],
)
java\_library(
name = "cafe",
visibility = ["//visibility:public"],
srcs = [
"Cafe.java",
],
deps = [
":chef",
],
)
再次建構 cafe
,確認沒有任何錯誤。如果成功建構成功,恭喜您!已為「咖啡廳」新增依附元件。如果不是的話,請留意是否有拼字錯誤和套件命名方式。如要進一步瞭解如何編寫 BUILD
檔案,請參閱「建構樣式指南」。
現在,透過新增 Smoothie
來與先前的圖表比較,以視覺化方式呈現新的依附元件圖表。為求明確,請將圖表輸入內容命名為 graph2.in
和 graph2.png
。
bazel query --noimplicit_deps 'deps(:runner)' --output graph > graph2.in
dot -Tpng < graph2.in > graph2.png
在 graph2.png
中,您可以看到 Smoothie
與其他餐點沒有共用依附元件,只是 Chef
依賴的另一個目標。
somepath() 和 allpaths()
如要查詢為何某個套件依附於另一個套件,該怎麼做?顯示兩者之間的依附元件路徑就能獲得解答。
以下兩個函式可協助您找出依附元件路徑:somepath()
和 allpaths()
。如果有起始目標 S 和終點 E,請使用 somepath(S,E)
找出 S 和 E 之間的路徑。
看看「主廚」間的關係,探索這兩項功能的差異和「Cheese」目標。以下是從一個目標到另一個目標的不同可能路徑:
- 主廚 → MacAndCheese → 起司
- 主廚 → 披薩 → 起司
somepath()
可讓您透過兩個選項取得單一路徑,「allpaths()」會輸出每個可能的路徑
以 Cafe Bazel 做為範例,執行下列指令:
bazel query "somepath(//src/main/java/com/example/restaurant/..., //src/main/java/com/example/ingredients:cheese)"
//src/main/java/com/example/restaurant:cafe
//src/main/java/com/example/restaurant:chef
//src/main/java/com/example/dishes:macAndCheese
//src/main/java/com/example/ingredients:cheese
輸出內容遵循咖啡廳的第一路徑 → 主廚 → MacAndCheese → 起司。反之,如果使用 allpaths()
,您可以得到:
bazel query "allpaths(//src/main/java/com/example/restaurant/..., //src/main/java/com/example/ingredients:cheese)"
//src/main/java/com/example/dishes:macAndCheese
//src/main/java/com/example/dishes:pizza
//src/main/java/com/example/ingredients:cheese
//src/main/java/com/example/restaurant:cafe
//src/main/java/com/example/restaurant:chef
allpaths()
的輸出內容是經過整併的依附元件清單,因此較難讀取。使用 Graphviz 以視覺化方式呈現這張圖表,可更清楚地瞭解關係。
測試自己
使用 Cafe Bazel 的某位客戶就對該餐廳有第一則評論!很抱歉,該評論缺少部分詳細資料,例如評論者的身分及提及出處。不過別擔心,您可以使用 Bazel 存取這項資訊。reviews
套件包含一個程式,可列印來自神秘客戶的評論。使用下列指令建構及執行應用程式:
bazel build //src/main/java/com/example/reviews:review
bazel-bin/src/main/java/com/example/reviews/review
因此只執行 Bazel 查詢,試著找出評論作者,以及評論內容。
提示
如需實用資訊,請查看標記和依附元件。
解答
這則評論是關於披薩店和阿米的描述,如要瞭解這項規則有哪些依附關係
bazel query --noimplicit\_deps 'deps(//src/main/java/com/example/reviews:review)'
這項指令的結果指出 Amir 是評論者!
接下來,由於你知道評論者 Amir,因此可以使用查詢函式找出 `BUILD` 檔案中哪個標記,查看該標記含有什麼餐點。
bazel query 'attr(tags, "pizza", //src/main/java/com/example/customers/...)'
指令會輸出小美是唯一訂購披薩的客戶,負責提供答案。
設定程序即將完成
恭喜!您現在已執行幾項基本查詢,可在自己的專案中試用。如要進一步瞭解查詢語言語法,請參閱查詢參考頁面。想取得更多進階查詢嗎?查詢指南含有更深入的用途清單,其中有哪些用途不在本指南的涵蓋範圍內。