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 tuỳ chọn bản dựng trên biểu đồ bản dựng.
Công cụ này thực hiện việc này bằng cách chạy trên kết quả của giai đoạn phân tích
của Bazel,
tích hợp các hiệu ứng này. Ngược lại, query chạy trên kết quả của giai đoạn tải của Bazel, trước khi các tuỳ 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 riêng (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ì đây không phải là các mục tiêu đã định cấu hình. Đối với mục tiêu trước, hãy xem aquery.
Cú pháp cơ bản
Một lệnh gọi cquery đơn giản sẽ 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ácquery's hàm, cộng với một số hàm mới.//targetlà biểu thức được cung cấp cho 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 các hàm. Hãy xem Hướng dẫn về 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 chỉ đị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 mờ của các giá trị tuỳ 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, 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 //foo đã định cấu hình.
Đố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 đó. Đầu ra là
xác định, nhưng cquery không đảm bảo thứ tự nào 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 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, trừ visible,
siblings, buildfiles,
và tests.
cquery cũng giới thiệu các hàm mới sau đây:
cấu hình
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 được chỉ định bằng đối số thứ hai.
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 đầ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
Tuỳ chọn bản dựng
cquery chạy trên bản dựng Bazel thông thường và do đó kế thừa tập hợp các
tuỳ chọn có sẵn trong quá trình xây dựng.
Sử dụng các tuỳ 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 các 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"],
)
| 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 bạn đặt cờ này, nội dung của cờ sẽ được xây dựng. Nếu bạn không đặt cờ này, 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 thay vào đó. 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 tuỳ 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 bạn có thể xây dựng tất cả các mục tiêu trong biểu thức truy vấn ở cấp cao nhất, thì bạn cũng không nên làm như vậy. Ví dụ: việc đặt rõ ràng --universe_scope 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 cách khác). Bạn nên đặt cờ này nếu biểu thức truy vấn của bạ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 bạn tắt cờ này, 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 sẽ 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. Bạn cũng có thể sử dụng các tuỳ 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ả cá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 tuỳ chọn bản dựng.
Định dạng đầu ra này được kích hoạt bằng cờ --transitions, cờ này 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 tuỳ 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ề các tuỳ chọn.
Đầu ra thông báo giao thức
--output=proto
Tuỳ 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ể xem đị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 trong thông báo Configuration tương ứng.
--[no]proto:include_configurations
Theo mặc định, kết quả cquery sẽ 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 đầu ra proto được định dạng giống hệt như đầ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ề đầu ra proto của truy vấn để biết thêm các tuỳ chọn liên quan đến đầu ra proto.
Đầu ra biểu đồ
--output=graph
Tuỳ chọn này tạo đầu ra dưới dạng tệp .dot tương thích với Graphviz. Hãy xem tài liệu về đầ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
Tuỳ 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 tạo ra, tương tự như danh sách được in ở cuối lệnh gọi bazel build. Đầ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
theo cờ
--output_groups.
Đầu ra 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 symlink tiện lợi bazel-out tồn tại, thì các đường dẫn đến tệp trong kho lưu trữ chính cũng phân giải liên quan đến 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 chỉ có thể chỉ định nội dung của 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 cốt lõi của Starlark
,
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 tuỳ 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 tuỳ chọn bản dựng có giá trị không phải là giá trị Starlark hợp lệ sẽ bị bỏ qua trong bản đồ này.
Nếu mục tiêu là tệp đầu vào, 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 trong 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 đầu ra 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 tuỳ 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 chỉ có một đầ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 chỉ 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, sẽ ước tính quá mức bằng cách bao gồm tất cả các nhánh.- Độ chính xác của
cqueryyêu cầu xây dựng nhiều biểu đồ hơnqueryCụ 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. - Việc diễn giải
ngôn ngữ truy vấn của
cquery's tạo 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 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 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
các quá trình chuyển đổi cạnh đến.
Đây là điểm mà cquery còn thiếu.
Giải pháp: Nếu có thể, hãy đặt --universe_scope thành 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.
Đầ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ễ bị chọn kết quả từ các truy vấn trước. 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 đổi mọi tuỳ 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 không chính xác rằng gói //foo không nằm trong phạm vi ngay cả khi
--universe_scope=//foo:app bao gồm gói này. Điều này là do các hạn chế về thiết kế trong cquery. Để khắc phục, 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 "+" -))"