Bắt đầu nhanh truy vấn

Sử dụng bộ sưu tập để sắp xếp ngăn nắp các trang Lưu và phân loại nội dung dựa trên lựa chọn ưu tiên của bạn.

Hướng dẫn này trình bày cách làm việc với Bazel để theo dõi các phần phụ thuộc trong mã bằng một dự án Bazel được tạo sẵn.

Để biết chi tiết về ngôn ngữ và cờ --output, hãy xem hướng dẫn Tài liệu tham khảo về truy vấn BazelTài liệu tham khảo truy vấn cazel. Nhận trợ giúp trong IDE bằng cách nhập bazel help query hoặc bazel help cquery trên dòng lệnh.

Mục tiêu

Hướng dẫn này trình bày một tập hợp các truy vấn cơ bản mà bạn có thể sử dụng để tìm hiểu thêm về các phần phụ thuộc tệp của dự án. Sách này dành cho những nhà phát triển mới của Bazel có kiến thức cơ bản về cách thức hoạt động của các tệp Bazel và BUILD.

Điều kiện tiên quyết

Hãy bắt đầu bằng cách cài đặt Bazel, nếu bạn chưa cài đặt. Hướng dẫn này sử dụng Git để kiểm soát nguồn, do đó, để có kết quả tốt nhất, bạn cũng nên cài đặt Git.

Để trực quan hóa biểu đồ phần phụ thuộc, hệ thống sẽ dùng công cụ Graphviz để bạn có thể tải xuống để theo dõi.

Tải dự án mẫu

Tiếp theo, truy xuất ứng dụng mẫu từ kho lưu trữ Ví dụ của Bielel bằng cách chạy nội dung sau trong công cụ dòng lệnh mà bạn chọn:

git clone https://github.com/bazelbuild/examples.git

Dự án mẫu cho hướng dẫn này nằm trong thư mục "examples/query-quickstart".

Bắt đầu

Truy vấn của Bazel là gì?

Các truy vấn giúp bạn tìm hiểu về cơ sở mã Bazel bằng cách phân tích mối quan hệ giữa các tệp BUILD và kiểm tra kết quả đầu ra để biết thông tin hữu ích. Hướng dẫn này xem trước một số hàm truy vấn cơ bản, nhưng để xem thêm tuỳ chọn, hãy xem Hướng dẫn truy vấn. Các truy vấn giúp bạn tìm hiểu về các phần phụ thuộc trong những dự án có quy mô lớn mà không cần phải điều hướng thủ công qua các tệp BUILD.

Để chạy một truy vấn, hãy mở dòng lệnh của bạn rồi nhập: posix-terminal bazel query 'query_function'

Trường hợp

Hãy tưởng tượng một tình huống tìm hiểu sâu về mối quan hệ giữa Cafe Bazel và đầu bếp tương ứng. Quán cà phê này chỉ bán pizza, Mac và phô mai. Hãy xem cấu trúc dự án dưới đây:

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

Trong suốt hướng dẫn này, trừ khi có hướng dẫn khác, hãy cố gắng không xem trong các tệp BUILD để tìm thông tin bạn cần mà thay vào đó chỉ sử dụng hàm truy vấn.

Một dự án bao gồm các gói khác nhau tạo nên một quán cà phê. Các mục này được phân tách thành: restaurant, ingredients, dishes, customersreviews. Các quy tắc trong những gói này xác định các thành phần khác nhau của Quán cà phê bằng nhiều thẻ và phần phụ thuộc.

Chạy bản dựng

Để bắt đầu, hãy xây dựng dự án này bằng Bazel để tạo tệp thực thi.

Tiếp theo, hãy chạy tệp thực thi để cung cấp thực đơn về các quán cà phê.

Để tạo dự án này, hãy dán lệnh sau vào thiết bị đầu cuối:

bazel build :runner

Kết quả của bạn sẽ có dạng như sau nếu bản dựng thành công. bash 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

Sau khi bạn tạo ứng dụng thành công, hãy chạy ứng dụng bằng cách dán lệnh sau:

bazel-bin/runner
--------------------- MENU -------------------------

Pizza - Cheesy Delicious Goodness
Macaroni & Cheese - Kid-approved Dinner

----------------------------------------------------

Khi đó, bạn sẽ có một danh sách các món trong thực đơn kèm theo nội dung mô tả ngắn.

Khám phá các mục tiêu

Dự án liệt kê các nguyên liệu và món ăn trong các gói riêng. Để sử dụng truy vấn để xem các quy tắc của gói, hãy chạy lệnh bazel query package/…

Trong trường hợp này, bạn có thể sử dụng mã này để xem qua các nguyên liệu và món ăn mà Quán cà phê này có bằng cách chạy:

bazel query //src/main/java/com/example/dishes/...
bazel query //src/main/java/com/example/ingredients/...

Nếu bạn truy vấn các mục tiêu của gói nguyên liệu, thì kết quả sẽ có dạng như sau:

//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

Tìm phần phụ thuộc

Trình chạy của bạn dựa vào mục tiêu nào để chạy?

Giả sử bạn muốn tìm hiểu sâu hơn về cấu trúc của dự án mà không cần thăm dò hệ thống tệp (hệ thống này có thể không sử dụng được với các dự án lớn). Quán cà phê Bazel sử dụng những quy tắc nào?

Nếu trong ví dụ này, mục tiêu cho trình chạy của bạn là runner, hãy khám phá các phần phụ thuộc cơ bản của mục tiêu bằng cách chạy lệnh:

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

Trong hầu hết các trường hợp, hãy sử dụng hàm truy vấn deps() để xem từng phần phụ thuộc đầu ra của một mục tiêu cụ thể.

Trực quan hoá biểu đồ phần phụ thuộc (không bắt buộc)

Phần này mô tả cách bạn có thể mô phỏng đường dẫn phần phụ thuộc của một truy vấn cụ thể. Graphviz giúp xem đường dẫn dưới dạng hình ảnh đồ thị không chu trình được hướng thay vì danh sách phẳng. Bạn có thể thay đổi chế độ hiển thị biểu đồ truy vấn Bazel bằng cách sử dụng các tùy chọn dòng lệnh --output khác nhau. Hãy xem phần Định dạng đầu ra để biết các tùy chọn.

Bắt đầu bằng cách chạy truy vấn bạn muốn và thêm cờ --noimplicit_deps để xoá các phần phụ thuộc công cụ quá mức. Sau đó, hãy theo dõi truy vấn bằng cờ đầu ra và lưu biểu đồ vào tệp có tên graph.in để tạo biểu diễn văn bản của biểu đồ.

Cách tìm tất cả phần phụ thuộc của :runner đích và định dạng kết quả đầu ra dưới dạng biểu đồ:

bazel query --noimplicit_deps 'deps(:runner)' --output graph > graph.in

Thao tác này sẽ tạo một tệp có tên là graph.in, tệp này biểu thị văn bản của biểu đồ bản dựng. Graphviz sử dụng dot – một công cụ xử lý văn bản thành hình ảnh trực quan – để tạo một tệp png:

dot -Tpng < graph.in > graph.png

Nếu mở graph.png ra, bạn sẽ thấy nội dung tương tự. Biểu đồ bên dưới đã được đơn giản hóa để làm rõ chi tiết đường dẫn quan trọng trong hướng dẫn này.

Sơ đồ minh họa mối quan hệ từ quán cà phê đến đầu bếp với các món ăn: pizza, bánh mì mac và phô mai phân biệt các thành phần riêng biệt: phô mai, cà chua, bột nhào và bánh macaroni.

Điều này giúp ích khi bạn muốn xem kết quả của các hàm truy vấn trong toàn bộ hướng dẫn này.

Tìm phần phụ thuộc đảo ngược

Thay vào đó, nếu bạn có mục tiêu mà bạn muốn phân tích xem mục tiêu nào khác sử dụng mục tiêu đó, thì bạn có thể sử dụng truy vấn để kiểm tra xem mục tiêu nào phụ thuộc vào một quy tắc nhất định. Đây được gọi là "phần phụ thuộc ngược". Việc sử dụng rdeps() có thể hữu ích khi chỉnh sửa một tệp trong cơ sở mã mà bạn không quen thuộc và có thể giúp bạn không vô tình làm hỏng các tệp khác phụ thuộc vào tệp đó.

Ví dụ: bạn muốn chỉnh sửa nguyên liệu của cheese. Để tránh gây ra vấn đề cho quán cà phê Bazel, bạn cần kiểm tra các món ăn dựa trên cheese.

Để xem mục tiêu nào phụ thuộc vào một mục tiêu/gói cụ thể, bạn có thể sử dụng rdeps(universe_scope, target). Hàm truy vấn rdeps() nhận ít nhất hai đối số: universe_scope – thư mục có liên quan – và target. Bazel tìm phần phụ thuộc đảo ngược của mục tiêu trong universe_scope được cung cấp. Toán tử rdeps() chấp nhận một đối số thứ ba không bắt buộc: một giá trị cố định kiểu số nguyên xác định giới hạn trên của chiều sâu của kết quả tìm kiếm.

Để tìm các phần phụ thuộc đảo ngược của cheese mục tiêu trong phạm vi của toàn bộ dự án ‘//...’, hãy chạy lệnh:

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

Kết quả trả về cho thấy rằng phô mai dựa vào cả pizza và macAndCheese. Thật bất ngờ!

Tìm mục tiêu dựa trên thẻ

Hai khách hàng bước vào quán cà phê Bazel: Amir và Jenny. Họ không biết gì về họ ngoại trừ tên của họ. May mắn thay, đơn đặt hàng của họ đã được gắn thẻ trong tệp BUILD "khách hàng". Bạn có thể truy cập vào thẻ này bằng cách nào?

Các nhà phát triển có thể gắn thẻ các mục tiêu của Bazel bằng các giá trị nhận dạng khác nhau, thường là để thử nghiệm. Ví dụ: các thẻ trên kiểm thử có thể chú thích cho vai trò của kiểm thử trong quá trình gỡ lỗi và phát hành, đặc biệt là đối với các kiểm thử C++ và Python, vốn không có khả năng chú thích thời gian chạy. Việc sử dụng thẻ và các phần tử kích thước cho phép tập hợp các bộ kiểm thử theo cách linh hoạt dựa trên chính sách đăng ký của cơ sở mã.

Trong ví dụ này, các thẻ là một trong những pizza hoặc macAndCheese để đại diện cho các mục trong trình đơn. Lệnh này truy vấn các mục tiêu có thẻ khớp với giá trị nhận dạng của bạn trong một gói nhất định.

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

Truy vấn này trả về tất cả mục tiêu trong gói 'khách hàng' có thẻ "pizza".

Tự kiểm tra

Dùng cụm từ tìm kiếm này để tìm hiểu Jenny muốn đặt món gì.

Câu trả lời

Mac và Pho mát

Thêm phần phụ thuộc mới

Quán cà phê Bazel đã mở rộng thực đơn — khách hàng hiện có thể đặt món Smoothie! Món sinh tố cụ thể này bao gồm các thành phần StrawberryBanana.

Trước tiên, hãy thêm các nguyên liệu mà sinh tố phụ thuộc: Strawberry.javaBanana.java. Thêm các lớp Java trống.

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 {

}

Tiếp theo, hãy thêm Smoothie.java vào thư mục thích hợp: 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";
}

Cuối cùng, hãy thêm các tệp này làm quy tắc trong các tệp BUILD thích hợp. Tạo một thư viện java mới cho từng thành phần mới, bao gồm cả tên, chế độ hiển thị công khai và tệp "src" mới tạo. Bạn nên kết thúc bằng tệp BUILD mới này:

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"],
)

Trong tệp BUILD cho món ăn, bạn muốn thêm một quy tắc mới cho Smoothie. Cách làm này bao gồm tệp Java được tạo cho Smoothie dưới dạng tệp "src" và các quy tắc mới mà bạn đã thực hiện cho mỗi thành phần của sinh tố.

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",
    ],
)

Cuối cùng, bạn muốn đưa sinh tố vào phần phụ thuộc BUILD của đầu bếp.

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",
    ],
)

Tạo lại cafe để xác nhận rằng không có lỗi. Nếu xây dựng thành công, xin chúc mừng! Bạn đã thêm một phần phụ thuộc mới cho "Cafe". Nếu không, hãy chú ý lỗi chính tả và cách đặt tên gói. Để biết thêm thông tin về cách viết tệp BUILD, hãy xem Hướng dẫn về quy tắc tạo dựng.

Bây giờ, hãy hình ảnh hóa biểu đồ phần phụ thuộc mới bằng cách thêm Smoothie để so sánh với biểu đồ trước đó. Để rõ ràng hơn, hãy đặt tên đầu vào biểu đồ là graph2.ingraph2.png.

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

Biểu đồ này giống với biểu đồ đầu tiên, ngoại trừ bây giờ có một món ăn xuất phát từ mục tiêu của đầu bếp bằng món sinh tố dẫn đến chuối và dâu

Nhìn vào graph2.png, bạn có thể thấy Smoothie không có phần phụ thuộc dùng chung nào với các món ăn khác mà chỉ là một mục tiêu khác mà Chef dựa vào.

somepath() và allpaths()

Nếu bạn muốn truy vấn lý do một gói phụ thuộc vào gói khác thì sao? Hiển thị đường dẫn phần phụ thuộc giữa hai đường dẫn này sẽ đưa ra câu trả lời.

Hai hàm này có thể giúp bạn tìm thấy các đường dẫn phần phụ thuộc: somepath()allpaths(). Khi tìm được mục tiêu S và điểm cuối E, hãy tìm đường dẫn giữa S và E bằng cách sử dụng somepath(S,E).

Khám phá sự khác biệt giữa hai hàm này bằng cách xem xét mối quan hệ giữa các mục tiêu "Đầu bếp" và "Pheese". Có nhiều đường dẫn khả thi để chuyển từ mục tiêu này sang mục tiêu khác:

  • Đầu bếp → MacAndCheese → Pho mát
  • Đầu bếp → Pizza → Pho mát

somepath() cung cấp cho bạn một đường dẫn duy nhất trong số hai tùy chọn này, trong khi "allpaths()" xuất ra mọi đường dẫn có thể có.

Lấy Ví dụ về Cafe Bazel, hãy chạy nội dung sau:

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

Đầu ra đi theo đường dẫn đầu tiên của Cafe → Chef → MacAndCheese → Pho mát. Nếu sử dụng allpaths(), bạn sẽ nhận được:

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

Đường đầu ra của quán cà phê sang đầu bếp pizza,mac và phô mai thành phô mai

Kết quả của allpaths() khó đọc hơn một chút vì đây là danh sách phẳng của các phần phụ thuộc. Hình ảnh đồ thị này sử dụng Graphviz giúp cho mối quan hệ trở nên dễ hiểu hơn.

Tự kiểm tra

Một trong những khách hàng của Cafe Bazel đã đánh giá đầu tiên về nhà hàng! Rất tiếc, bài đánh giá thiếu một số thông tin chi tiết như danh tính của người đánh giá và món ăn mà họ đang đề cập. May mắn thay, bạn có thể truy cập thông tin này với Bazel. Gói reviews chứa một chương trình in bài đánh giá của một khách hàng bí ẩn. Tạo và chạy công cụ này bằng:

bazel build //src/main/java/com/example/reviews:review
bazel-bin/src/main/java/com/example/reviews/review

Chỉ truy vấn Bazel, hãy cố gắng tìm xem ai đã viết bài đánh giá và món ăn nào họ đang mô tả.

Gợi ý

Kiểm tra các thẻ và phần phụ thuộc để biết thông tin hữu ích.

Câu trả lời

Bài đánh giá này mô tả về Pizza và Amir là người đánh giá. Nếu bạn xem các phần phụ thuộc mà quy tắc này sử dụng bazel query --noimplicit\_deps 'deps(//src/main/java/com/example/reviews:review)' Kết quả của lệnh này cho thấy Amir là người đánh giá! Tiếp theo, vì bạn biết người đánh giá này là Amir, nên bạn có thể sử dụng hàm truy vấn để tìm thẻ Amir có trong tệp "BUILD" để xem món ăn đó là gì. Lệnh bazel query 'attr(tags, "pizza", //src/main/java/com/example/customers/...)' đưa ra kết quả rằng Amir là khách hàng duy nhất đã đặt pizza và là người đánh giá đưa ra câu trả lời cho chúng tôi.

Đang hoàn tất

Xin chúc mừng! Hiện tại, bạn đã chạy một số truy vấn cơ bản mà bạn có thể thử trên các dự án của riêng mình. Để tìm hiểu thêm về cú pháp ngôn ngữ truy vấn, hãy tham khảo trang Tham chiếu truy vấn. Bạn muốn có thêm cụm từ tìm kiếm nâng cao? Hướng dẫn truy vấn trình bày danh sách chi tiết về nhiều trường hợp sử dụng hơn so với đề cập trong hướng dẫn này.