查詢快速入門導覽課程

回報問題 查看原始碼 Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

本教學課程將說明如何使用預先製作的 Bazel 專案,搭配 Bazel 追蹤程式碼中的依附元件。

如要進一步瞭解語言和 --output 標記,請參閱 Bazel 查詢參考資料Bazel cquery 參考資料手冊。在指令列中輸入 bazel help querybazel help cquery,即可在 IDE 中取得說明。

目標

本指南會逐步說明一組基本查詢,您可以利用這些查詢進一步瞭解專案的檔案依附元件。本指南適用於新手 Bazel 開發人員,他們對 Bazel 和 BUILD 檔案的運作方式有基本瞭解。

必要條件

首先,請安裝 Bazel (如果尚未安裝)。本教學課程使用 Git 進行原始碼控管,因此建議您一併安裝 Git,以便取得最佳結果。

如要將依附元件圖表製作成圖表,請使用 Graphviz 工具下載,跟上圖一樣。

取得範例專案

接下來,請在您選擇的指令列工具中執行下列指令,從 Bazel 的範例存放區中擷取範例應用程式:

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
└── WORKSPACE

在本教學課程中,除非另有指示,否則請勿在 BUILD 檔案中尋找所需資訊,而是只使用查詢函式。

專案包含組成 Cafe 的不同套件。這些類別分為:restaurantingredientsdishescustomersreviews。這些套件中的規則會定義 Cafe 的不同元件,並附上各種標記和依附元件。

執行建構作業

這項專案在 Runner.java 內包含主要方法,您可以執行以顯示 Cafe 選單。搭配 bazel build 指令使用 Bazel 建構專案,並使用 : 表示目標名稱為 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

查詢傳回結果顯示,pizza 和 macAndCheese 都需要起司。真是驚喜!

根據代碼尋找目標

兩位顧客走進 Bazel Cafe:Amir 和 Jenny。他們一無所知,只有其名字才知道。值得慶幸的是,他們已在「客戶」的 BUILD 檔案中標記訂單。如何存取這個代碼?

開發人員可以使用不同 ID 標記 Bazel 目標,通常用於測試。舉例來說,測試標記可在偵錯和發布程序中標註測試的角色,特別適用於缺乏任何執行階段標註功能的 C++ 和 Python 測試。使用標記和大小元素,可根據程式碼集的簽入政策,靈活組合測試套件。

在本例中,標記為 pizzamacAndCheese 之一,用來代表選單項目。這個指令會在特定套件中查詢標記與 ID 相符的目標。

bazel query 'attr(tags, "pizza", //src/main/java/com/example/customers/...)'

這個查詢會傳回「customers」套件中標記為「pizza」的所有目標。

自行測試

使用這個查詢,瞭解 Jenny 想訂購的商品。

解答

起司通心粉

新增依附元件

Cafe Bazel 的菜單擴大了,現在顧客可以點購果昔了!這種冰沙包括成分 StrawberryBanana

首先,請新增平滑化的成分:Strawberry.javaBanana.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",
    ],
)

最後,您要將 smoothie 納入 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,確認沒有錯誤。如果建構成功,恭喜!您已為「Cafe」新增依附元件。如果不是,請留意拼字錯誤和套件命名。如要進一步瞭解如何編寫 BUILD 檔案,請參閱 BUILD 樣式指南

現在,透過新增 Smoothie 來與先前的圖表比較,以視覺化方式呈現新的依附元件圖表。為求明確,請將圖表輸入內容命名為 graph2.ingraph2.png

bazel query --noimplicit_deps 'deps(:runner)' --output graph > graph2.in
dot -Tpng < graph2.in > graph2.png

與第一張圖相同,但現在有一個從「smoothie」的廚師目標延伸的輪輻,可連到香蕉和草莓

查看 graph2.png 時,您會發現 Smoothie 與其他菜餚沒有共用依附元件,而是 Chef 所依賴的另一個目標。

somepath() 和 allpaths()

如果您想查詢某個套件為何依附另一個套件,顯示兩者之間的依附路徑即可提供答案。

somepath()allpaths() 這兩個函式可協助您找出依附元件路徑。如果有起始目標 S 和終點 E,請使用 somepath(S,E) 找出 S 和 E 之間的路徑。

請查看「Chef」和「Cheese」目標之間的關係,瞭解這兩個函式之間的差異。從一個目標到另一個目標,可能會經過不同的路徑:

  • Chef → MacAndCheese → Cheese
  • Chef → Pizza → Cheese

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 將這張圖表視覺化,就能更清楚瞭解兩者之間的關係。

自行測試

咖啡廳 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` 檔案中 Amir 有哪些標記,並瞭解其中的菜色。指令 bazel query 'attr(tags, "pizza", //src/main/java/com/example/customers/...)' 的輸出內容指出,Amir 是唯一訂購披薩的顧客,也是提供答案的評論者。

設定程序即將完成

恭喜!您現在已執行幾個基本查詢,可以嘗試在自己的專案中使用。如要進一步瞭解查詢語言語法,請參閱查詢參考資料頁面。想取得更多進階查詢嗎?查詢指南含有更深入的用途清單,其中有哪些用途不在本指南的涵蓋範圍內。