cquery là một biến thể của query có thể xử lý chính xác các hiệu ứng của
select() và các lựa chọn bản dựng trên biểu đồ bản dựng.
Công cụ này đạt được điều đó bằng cách chạy trên kết quả của giai đoạn phân tích
Bazel,
tích hợp các hiệu ứng này. query Ngược lại, chạy trên kết quả của
giai đoạn tải Bazel, trước khi các lựa chọn được đánh giá.
Ví dụ:
$ 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)
Mỗi kết quả bao gồm một giá trị nhận dạng duy nhất (9f87702) của
cấu hình mà
mục tiêu được xây dựng.
Vì cquery chạy trên biểu đồ mục tiêu đã định cấu hình, nên công cụ này không có thông tin chi tiết
về các cấu phần phần mềm như hành động bản dựng cũng như không có quyền truy cập vào test_suite
quy tắc vì chúng không phải là mục tiêu đã định cấu hình. Đối với mục tiêu trước đây, hãy xem aquery.
Cú pháp cơ bản
Một lệnh gọi cquery đơn giản có dạng như sau:
bazel cquery "function(//target)"
Biểu thức truy vấn "function(//target)" bao gồm những nội dung sau:
function(...)là hàm cần chạy trên mục tiêu.cqueryhỗ trợ hầu hết các hàm củaquery's functions, cùng với một số hàm mới.//targetlà biểu thức được đưa vào hàm. Trong ví dụ này, biểu thức là một mục tiêu đơn giản. Tuy nhiên, ngôn ngữ truy vấn cũng cho phép lồng ghép các hàm. Hãy xem hướng dẫn Truy vấn để biết các ví dụ.
cquery yêu cầu một mục tiêu chạy qua các giai đoạn tải và phân tích. Trừ phi có quy định khác, cquery sẽ phân tích cú pháp(các) mục tiêu được liệt kê trong
biểu thức truy vấn. Hãy xem --universe_scope
để truy vấn các phần phụ thuộc của mục tiêu bản dựng cấp cao nhất.
Cấu hình
Dòng:
//tree:ash (9f87702)
có nghĩa là //tree:ash được xây dựng trong một cấu hình có mã nhận dạng 9f87702. Đối với hầu hết
các mục tiêu, đây là một hàm băm không rõ ràng của các giá trị lựa chọn bản dựng xác định
cấu hình.
Để xem nội dung hoàn chỉnh của cấu hình, hãy chạy:
$ bazel config 9f87702
9f87702 là tiền tố của mã nhận dạng hoàn chỉnh. Điều này là do mã nhận dạng hoàn chỉnh là
các hàm băm SHA-256, có độ dài và khó theo dõi. cquery hiểu mọi tiền tố hợp lệ của mã nhận dạng hoàn chỉnh, tương tự như các hàm băm ngắn của Git.
Để xem mã nhận dạng hoàn chỉnh, hãy chạy $ bazel config.
Đánh giá mẫu mục tiêu
//foo có ý nghĩa khác đối với cquery so với query. Điều này là do
cquery đánh giá các mục tiêu đã định cấu hình và biểu đồ bản dựng có thể có nhiều phiên bản
đã định cấu hình của //foo.
Đối với cquery, một mẫu mục tiêu trong biểu thức truy vấn sẽ đánh giá
mọi mục tiêu đã định cấu hình có nhãn phù hợp với mẫu đó. Kết quả là
xác định, nhưng cquery không đảm bảo thứ tự ngoài
hợp đồng đặt hàng truy vấn cốt lõi.
Điều này tạo ra kết quả tinh tế hơn cho các biểu thức truy vấn so với query.
Ví dụ: nội dung sau đây có thể tạo ra nhiều kết quả:
# Analyzes //foo in the target configuration, but also analyzes # //genrule_with_foo_as_tool which depends on an exec-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 (exec)
Nếu bạn muốn khai báo chính xác thực thể cần truy vấn, hãy sử dụng
hàm config.
Hãy xem tài liệu về mẫu mục tiêu của query's mẫu mục tiêu
tài liệu để biết thêm thông tin về các mẫu mục tiêu.
Hàm
Trong tập hợp các hàm
được query hỗ trợ, cquery hỗ trợ tất cả các hàm, ngoại trừ
allrdeps,
buildfiles,
rbuildfiles,
siblings, tests và
visible.
cquery cũng giới thiệu các hàm mới sau đây:
config
expr ::= config(expr, word)
Toán tử config cố gắng tìm mục tiêu đã định cấu hình cho
nhãn được biểu thị bằng đối số đầu tiên và cấu hình do đối số thứ hai chỉ định.
Các giá trị hợp lệ cho đối số thứ hai là null hoặc hàm băm cấu hình tuỳ chỉnh
. Bạn có thể truy xuất các hàm băm từ $
bazel config hoặc kết quả đầu ra của cquery trước đó.
Ví dụ:
$ bazel cquery "config(//bar, 3732cc8)" --universe_scope=//foo
$ bazel cquery "deps(//foo)" //bar (exec) //baz (exec) $ bazel cquery "config(//baz, 3732cc8)"
Nếu không tìm thấy tất cả kết quả của đối số đầu tiên trong cấu hình đã chỉ định, thì chỉ những kết quả có thể tìm thấy mới được trả về. Nếu không tìm thấy kết quả nào trong cấu hình đã chỉ định, thì truy vấn sẽ không thành công.
Tùy chọn
Lựa chọn bản dựng
cquery chạy trên một bản dựng Bazel thông thường và do đó kế thừa tập hợp các
lựa chọn có sẵn trong quá trình bản dựng.
Sử dụng các lựa chọn cquery
--universe_scope (danh sách được phân tách bằng dấu phẩy)
Thông thường, các phần phụ thuộc của mục tiêu đã định cấu hình sẽ trải qua quá trình chuyển đổi, khiến cấu hình của chúng khác với phần phụ thuộc. Cờ này cho phép bạn truy vấn một mục tiêu như thể mục tiêu đó được xây dựng dưới dạng phần phụ thuộc hoặc phần phụ thuộc bắc cầu của một mục tiêu khác. Ví dụ:
# x/BUILD
genrule(
name = "my_gen",
srcs = ["x.in"],
outs = ["x.cc"],
cmd = "$(locations :tool) $< >$@",
tools = [":tool"],
)
cc_binary(
name = "tool",
srcs = ["tool.cpp"],
)
Genrules định cấu hình các công cụ của chúng trong cấu hình exec để các truy vấn sau đây tạo ra kết quả đầu ra sau:
| Truy vấn | Mục tiêu đã xây dựng | Đầu ra |
|---|---|---|
| bazel cquery "//x:tool" | //x:tool | //x:tool(targetconfig) |
| bazel cquery "//x:tool" --universe_scope="//x:my_gen" | //x:my_gen | //x:tool(execconfig) |
Nếu cờ này được đặt, nội dung của cờ sẽ được xây dựng. Nếu cờ này không được đặt, thì tất cả các mục tiêu
được đề cập trong biểu thức truy vấn sẽ được xây dựng. Bao đóng bắc cầu của các mục tiêu đã xây dựng
được dùng làm vũ trụ của truy vấn. Dù bằng cách nào, các mục tiêu cần
xây dựng phải có thể xây dựng ở cấp cao nhất (tức là tương thích với các lựa chọn cấp cao nhất
). cquery trả về kết quả trong bao đóng bắc cầu của các
mục tiêu cấp cao nhất này.
Ngay cả khi có thể xây dựng tất cả các mục tiêu trong một biểu thức truy vấn ở cấp cao nhất
, thì bạn cũng có thể không nên làm như vậy. Ví dụ: việc đặt
--universe_scope một cách rõ ràng có thể ngăn việc xây dựng mục tiêu nhiều lần trong
các cấu hình mà bạn không quan tâm. Điều này cũng có thể giúp chỉ định phiên bản cấu hình của mục tiêu mà bạn đang tìm kiếm (vì hiện tại, bạn không thể chỉ định đầy đủ theo bất kỳ cách nào khác). Bạn nên đặt cờ này
nếu biểu thức truy vấn phức tạp hơn deps(//foo).
--implicit_deps (boolean, default=True)
Việc đặt cờ này thành false sẽ lọc ra tất cả các kết quả không được đặt rõ ràng trong tệp BUILD và thay vào đó được Bazel đặt ở nơi khác. Điều này bao gồm việc lọc các chuỗi công cụ đã phân giải.
--tool_deps (boolean, default=True)
Việc đặt cờ này thành false sẽ lọc ra tất cả các mục tiêu đã định cấu hình mà đường dẫn từ mục tiêu được truy vấn đến các mục tiêu đó vượt qua quá trình chuyển đổi giữa cấu hình mục tiêu và các cấu hình không phải mục tiêu.
Nếu mục tiêu được truy vấn nằm trong cấu hình mục tiêu, thì việc đặt --notool_deps sẽ
chỉ trả về các mục tiêu cũng nằm trong cấu hình mục tiêu. Nếu mục tiêu được truy vấn
nằm trong cấu hình không phải mục tiêu, thì việc đặt --notool_deps sẽ chỉ trả về
các mục tiêu cũng nằm trong cấu hình không phải mục tiêu. Chế độ cài đặt này thường không ảnh hưởng đến việc lọc
các chuỗi công cụ đã phân giải.
--include_aspects (boolean, default=True)
Bao gồm các phần phụ thuộc do các khía cạnh thêm vào.
Nếu cờ này bị tắt, cquery somepath(X, Y) và
cquery deps(X) | grep 'Y' sẽ bỏ qua Y nếu X chỉ phụ thuộc vào Y thông qua một khía cạnh.
Định dạng đầu ra
Theo mặc định, cquery xuất kết quả trong danh sách các cặp nhãn và cấu hình được sắp xếp theo thứ tự phụ thuộc. Ngoài ra, còn có các lựa chọn khác để hiển thị kết quả.
Kiểu chuyển cảnh
--transitions=lite --transitions=full
Quá trình chuyển đổi cấu hình được dùng để xây dựng các mục tiêu bên dưới các mục tiêu cấp cao nhất trong các cấu hình khác với các mục tiêu cấp cao nhất.
Ví dụ: một mục tiêu có thể áp đặt quá trình chuyển đổi sang cấu hình exec trên tất cả
phần phụ thuộc trong thuộc tính tools. Đây được gọi là quá trình chuyển đổi thuộc tính. Các quy tắc cũng có thể áp đặt quá trình chuyển đổi trên cấu hình riêng của chúng,
được gọi là quá trình chuyển đổi lớp quy tắc. Định dạng đầu ra này xuất thông tin về
các quá trình chuyển đổi này, chẳng hạn như loại quá trình chuyển đổi và hiệu ứng của chúng đối với các lựa chọn bản dựng.
Định dạng đầu ra này được kích hoạt bởi cờ --transitions theo mặc định được
đặt thành NONE. Bạn có thể đặt thành chế độ FULL hoặc LITE. Chế độ FULL xuất
thông tin về quá trình chuyển đổi lớp quy tắc và quá trình chuyển đổi thuộc tính, bao gồm cả
sự khác biệt chi tiết của các lựa chọn trước và sau quá trình chuyển đổi. Chế độ LITE
xuất cùng thông tin mà không có sự khác biệt về lựa chọn.
Đầu ra thông báo giao thức
--output=proto
Lựa chọn này khiến các mục tiêu kết quả được in ở dạng vùng đệm giao thức nhị phân. Bạn có thể tìm thấy định nghĩa của vùng đệm giao thức tại src/main/protobuf/analysis_v2.proto.
CqueryResult là thông báo cấp cao nhất chứa kết quả của cquery. Thông báo này có danh sách các thông báo ConfiguredTarget và danh sách các thông báo Configuration. Mỗi ConfiguredTarget có một configuration_id có giá trị bằng
với giá trị của trường id từ thông báo Configuration tương ứng.
--[no]proto:include_configurations
Theo mặc định, kết quả cquery trả về thông tin cấu hình như một phần của mỗi mục tiêu đã định cấu hình. Nếu bạn muốn bỏ qua thông tin này và nhận kết quả đầu ra proto được định dạng giống hệt như kết quả đầu ra proto của truy vấn, hãy đặt cờ này thành false.
Hãy xem tài liệu về kết quả đầu ra proto của truy vấn để biết thêm các lựa chọn liên quan đến kết quả đầu ra proto.
Đầu ra biểu đồ
--output=graph
Lựa chọn này tạo ra kết quả đầu ra dưới dạng tệp .dot tương thích với Graphviz. Hãy xem tài liệu về kết quả đầu ra biểu đồ của query's
để biết thông tin chi tiết. cquery
cũng hỗ trợ --graph:node_limit và
--graph:factored.
Đầu ra tệp
--output=files
Lựa chọn này in danh sách các tệp đầu ra do mỗi mục tiêu được truy vấn so khớp tạo ra, tương tự như danh sách được in ở cuối lệnh gọi bazel build
Kết quả đầu ra chỉ chứa các tệp được quảng cáo trong các nhóm đầu ra được yêu cầu
do
--output_groups cờ xác định.
Kết quả này bao gồm các tệp nguồn.
Tất cả các đường dẫn do định dạng đầu ra này phát ra đều liên quan đến
execroot, có thể lấy được
thông qua bazel info execution_root. Nếu tồn tại liên kết tượng trưng tiện lợi bazel-out,
thì các đường dẫn đến tệp trong kho lưu trữ chính cũng phân giải tương ứng với thư mục không gian làm việc.
Xác định định dạng đầu ra bằng Starlark
--output=starlark
Định dạng đầu ra này gọi một hàm Starlark
cho mỗi mục tiêu đã định cấu hình trong kết quả truy vấn và in giá trị
do lệnh gọi trả về. Cờ --starlark:file chỉ định vị trí của tệp Starlark xác định một hàm có tên format với một tham số duy nhất là target. Hàm này được gọi cho mỗi Mục tiêu
trong kết quả truy vấn. Ngoài ra, để thuận tiện, bạn có thể chỉ định chỉ phần
nội dung của một hàm được khai báo là def format(target): return expr bằng cách sử dụng cờ
--starlark:expr.
Phương ngữ Starlark "cquery"
Môi trường Starlark cquery khác với tệp BUILD hoặc .bzl. Môi trường này bao gồm
tất cả các hằng số và hàm tích hợp sẵn Starlark
cốt lõi,
cùng với một số hằng số và hàm dành riêng cho cquery được mô tả bên dưới, nhưng không bao gồm (ví dụ) glob,
native, hoặc rule, và không hỗ trợ các câu lệnh tải.
build_options(target)
build_options(target) trả về một bản đồ có các khoá là giá trị nhận dạng lựa chọn bản dựng (xem
Cấu hình)
và các giá trị là giá trị Starlark của chúng. Các lựa chọn bản dựng có giá trị không phải là giá trị Starlark
hợp lệ sẽ bị bỏ qua khỏi bản đồ này.
Nếu mục tiêu là một tệp đầu vào, thì build_options(target) sẽ trả về None, vì các mục tiêu tệp đầu vào
có cấu hình rỗng.
providers(target)
providers(target) trả về một bản đồ có các khoá là tên của
nhà cung cấp
(ví dụ: "DefaultInfo") và các giá trị là giá trị Starlark của chúng. Các nhà cung cấp
có giá trị không phải là giá trị Starlark hợp lệ sẽ bị bỏ qua khỏi bản đồ này.
Ví dụ
In danh sách được phân tách bằng dấu cách gồm các tên cơ sở của tất cả các tệp do //foo tạo ra:
bazel cquery //foo --output=starlark \
--starlark:expr="' '.join([f.basename for f in target.files.to_list()])"
In danh sách được phân tách bằng dấu cách gồm các đường dẫn của tất cả các tệp do các mục tiêu quy tắc tạo ra trong
//bar và các gói con của nó:
bazel cquery 'kind(rule, //bar/...)' --output=starlark \
--starlark:expr="' '.join([f.path for f in target.files.to_list()])"
In danh sách các từ viết tắt của tất cả các hành động do //foo đăng ký.
bazel cquery //foo --output=starlark \
--starlark:expr="[a.mnemonic for a in target.actions]"
In danh sách các kết quả biên dịch do cc_library //baz đăng ký.
bazel cquery //baz --output=starlark \
--starlark:expr="[f.path for f in target.output_groups.compilation_outputs.to_list()]"
In giá trị của lựa chọn dòng lệnh --javacopt khi xây dựng //foo.
bazel cquery //foo --output=starlark \
--starlark:expr="build_options(target)['//command_line_option:javacopt']"
In nhãn của mỗi mục tiêu có đúng một kết quả đầu ra. Ví dụ này sử dụng các hàm Starlark được xác định trong một tệp.
$ 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
In nhãn của mỗi mục tiêu hoàn toàn là Python 3. Ví dụ này sử dụng các hàm Starlark được xác định trong một tệp.
$ 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
Trích xuất giá trị từ Nhà cung cấp do người dùng xác định.
$ 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 so với truy vấn
cquery và query bổ sung cho nhau và vượt trội trong
các lĩnh vực khác nhau. Hãy cân nhắc những điều sau để quyết định công cụ nào phù hợp với bạn:
cquerytuân theo các nhánhselect()cụ thể để mô hình hoá biểu đồ chính xác mà bạn xây dựng.querykhông biết nhánh nào mà bản dựng chọn, vì vậy, công cụ này sẽ ước tính quá mức bằng cách đưa vào tất cả các nhánh.- Độ chính xác của
cquery's yêu cầu xây dựng nhiều biểu đồ hơn so vớiquerydoes. Cụ thể,cqueryđánh giá các mục tiêu đã định cấu hình trong khiquerychỉ đánh giá các mục tiêu. Điều này mất nhiều thời gian hơn và sử dụng nhiều bộ nhớ hơn. cqueryViệc diễn giải ngôn ngữ truy vấn gây ra sự mơ hồ màquerytránh được. Ví dụ: nếu"//foo"tồn tại trong hai cấu hình, thì cấu hình nào nêncquery "deps(//foo)"sử dụng? Hàmconfigcó thể giúp bạn giải quyết vấn đề này.- Là một công cụ mới hơn,
cquerythiếu sự hỗ trợ cho một số trường hợp sử dụng. Hãy xem phần Các vấn đề đã biết để biết thông tin chi tiết.
Vấn đề đã biết
Tất cả các mục tiêu mà cquery "xây dựng" phải có cùng cấu hình.
Trước khi đánh giá các truy vấn, cquery sẽ kích hoạt một bản dựng ngay trước
thời điểm các hành động bản dựng sẽ thực thi. Theo mặc định, các mục tiêu mà công cụ này"xây dựng" được chọn từ tất cả các nhãn xuất hiện trong biểu thức truy vấn (bạn có thể ghi đè bằng --universe_scope). Các mục tiêu này phải có cùng cấu hình.
Mặc dù các mục tiêu này thường chia sẻ cấu hình "mục tiêu" cấp cao nhất,
nhưng các quy tắc có thể thay đổi cấu hình riêng của chúng bằng
quá trình chuyển đổi cạnh đến.
Đây là điểm mà cquery còn thiếu.
Giải pháp thay thế: Nếu có thể, hãy đặt --universe_scope thành một phạm vi nghiêm ngặt hơn. Ví dụ:
# 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
Không hỗ trợ --output=xml.
Kết quả đầu ra không xác định.
cquery không tự động xoá biểu đồ bản dựng khỏi
các lệnh trước đó và do đó, dễ chọn kết quả từ các
truy vấn trước đây. Ví dụ: genquery thực hiện quá trình chuyển đổi exec trên
thuộc tính tools – tức là định cấu hình các công cụ của nó trong
cấu hình exec.
Bạn có thể thấy các hiệu ứng kéo dài của quá trình chuyển đổi đó bên dưới.
$ 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 (exec_config)
...
$ bazel cquery "//foo:tool"
tool(exec_config)
Giải pháp thay thế: thay đổi mọi lựa chọn khởi động để buộc phân tích lại các mục tiêu đã định cấu hình.
Ví dụ: thêm --test_arg=<whatever> vào lệnh bản dựng.
Khắc phục sự cố
Mẫu mục tiêu đệ quy (/...)
Nếu bạn gặp phải:
$ 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.
điều này cho thấy gói //foo không nằm trong phạm vi, mặc dù
--universe_scope=//foo:app bao gồm gói đó. Điều này là do các giới hạn thiết kế trong
cquery. Để giải quyết vấn đề này, hãy đưa //foo/... vào phạm vi vũ trụ
một cách rõ ràng:
$ bazel cquery --universe_scope=//foo:app,//foo/... "somepath(//foo:app, //foo/...)"
Nếu cách đó không hiệu quả (ví dụ: vì một số mục tiêu trong //foo/... không thể
xây dựng bằng các cờ bản dựng đã chọn), hãy tự động giải gói mẫu thành các gói thành phần bằng truy vấn tiền xử lý:
# 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 "+" -))"