cquery
là một biến thể của query
xử lý chính xác select()
và các hiệu ứng của tuỳ chọn bản dựng trên biểu đồ bản dựng.
Công cụ này đạt được điều này bằng cách chạy qua 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 qua kết quả của giai đoạn tải 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 mã nhận dạng duy nhất (9f87702)
của cấu hình mà mục tiêu được tạo.
Vì cquery
chạy trên biểu đồ mục tiêu đã định cấu hình, nên không có thông tin chi tiết về các cấu phần phần mềm như hành động tạo bản dựng cũng như không truy cập được vào các quy tắc [test_suite](/versions/6.2.0/reference/be/general#test_suite)
vì chúng không phải là mục tiêu được định cấu hình. Đối với trường hợp trước, hãy xem [aquery](/versions/6.2.0/docs/aquery)
.
Cú pháp cơ bản
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:
function(...)
là hàm để chạy trên mục tiêu.cquery
hỗ trợ hầu hết các hàm củaquery
, cùng với một số hàm mới.//target
là 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 phần Cách truy vấn để biết 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. 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 tạo trong một cấu hình có mã 9f87702
. Đối với hầu hết các mục tiêu, đây là 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 toàn bộ nội dung của cấu hình, hãy chạy:
$ bazel config 9f87702
Cấu hình máy chủ sử dụng mã nhận dạng đặc biệt (HOST)
. Các tệp nguồn không được tạo, chẳng hạn như các tệp thường thấy trong srcs
, sử dụng mã nhận dạng đặc biệt (null)
(vì các tệp này không cần được định cấu hình).
9f87702
là tiền tố của mã nhận dạng đầy đủ. Lý do là mã nhận dạng đầy đủ là hàm băm SHA-256, có độ dài lớn và khó theo dõi. cquery
hiểu mọi tiền tố hợp lệ của một mã nhận dạng hoàn chỉnh, tương tự như hàm băm ngắn Git.
Để xem mã nhận dạng đầy đủ, hãy chạy $ bazel config
.
Đánh giá mẫu mục tiêu
//foo
có nghĩa khác với cquery
so với query
. Nguyên nhân 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ẫu mục tiêu trong biểu thức truy vấn sẽ đánh giá mọi mục tiêu được định cấu hình bằng một nhãn khớp với mẫu đó. Kết quả là cố định, nhưng cquery
không đảm bảo thứ tự ngoài hợp đồng thứ tự truy vấn cốt lõi.
Điều này tạo ra kết quả tinh tế hơn cho 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 a host-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 (HOST)
Nếu bạn muốn khai báo chính xác thực thể nào sẽ được 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
để biết thêm thông tin về mẫu mục tiêu.
Hàm
Trong nhóm hàm được query
hỗ trợ, cquery
hỗ trợ tất cả 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à target
, host
, null
hoặc hàm băm cấu hình tuỳ chỉnh. Bạn có thể truy xuất hàm băm từ $
bazel config
hoặc đầu ra của cquery
trước đó.
Ví dụ:
$ bazel cquery "config(//bar, host)" --universe_scope=//foo
$ bazel cquery "deps(//foo)" //bar (HOST) //baz (3732cc8) $ 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 được 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 tạo 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 tuỳ chọn có sẵn trong một bản 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 quá trình chuyển đổi, khiến cấu hình của các phần phụ thuộc này 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 tạo 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_library( name = "tool", )
Genrules định cấu hình các công cụ trong cấu hình máy chủ để các truy vấn sau đây sẽ cho ra kết quả sau:
Truy vấn | Tạo mục tiêu | Đầ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(hostconfig) |
Nếu bạn đặt cờ này thì nội dung của cờ sẽ được tạo. Nếu bạn không đặt thuộc tính này, tất cả các mục tiêu được đề cập trong biểu thức truy vấn sẽ được tạo. Đóng bắc cầu của các mục tiêu đã tạo được sử dụng làm thành phần chính của truy vấn. Dù bằng cách nào, các mục tiêu cần tạo phải có thể tạo ở 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 phép đó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ể tạo tất cả mục tiêu trong một biểu thức truy vấn ở cấp cao nhất, bạn vẫn 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 các mục tiêu tạo nhiều lần trong những cấu hình mà bạn không quan tâm. Tệp 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 không thể chỉ định đầy đủ phiên bản này theo cách nào 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, mặc định=True)
Việc đặt cờ này thành false sẽ lọc ra tất cả kết quả không được đặt rõ ràng trong tệp BUILD và thay vào đó, Bazel sẽ đặt ở nơi khác. Điều này bao gồm việc lọc các chuỗi công cụ đã được phân giải.
--tool_deps
(boolean, mặc định=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ấ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, mặc định=True)
Aspects (Phương diện) có thể thêm các phần phụ thuộc bổ sung vào một bản dựng. Theo mặc định, cquery
không tuân theo các khía cạnh vì các khía cạnh này làm cho biểu đồ có thể truy vấn lớn hơn, do đó sử dụng nhiều bộ nhớ hơn. Tuy nhiên, việc tuân theo các nguyên tắc này sẽ mang lại kết quả chính xác hơn.
Nếu bạn không lo ngại về mức tác động của bộ nhớ đối với các truy vấn lớn, hãy bật cờ này theo mặc định trong bazelrc.
Nếu truy vấn với các khía cạnh bị tắt, bạn có thể gặp phải vấn đề trong đó mục tiêu X không thành công trong khi tạo mục tiêu Y, nhưng cquery somepath(Y, X)
và cquery deps(Y) | grep 'X'
không trả về kết quả nào vì phần phụ thuộc xảy ra 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 phần phụ thuộc. Ngoài ra, còn có các tuỳ chọn khác để hiển thị kết quả.
Kiểu chuyển cảnh
--transitions=lite --transitions=full
Chuyển đổi cấu hình được dùng để tạo các mục tiêu bên dưới mục tiêu cấp cao nhất ở các cấu hình khác với 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 máy chủ 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 các chuyển đổi trên cấu hình của riêng chúng, còn gọi là chuyển đổi lớp quy tắc. Định dạng đầu ra này sẽ xuất thông tin về các hiệu ứng chuyển đổi này, chẳng hạn như loại hiệu ứng và ảnh hưởng của các hiệu ứ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
, theo mặc định được đặt thành NONE
. Bạn có thể đặt thành chế độ FULL
hoặc LITE
. Chế độ FULL
sẽ xuất thông tin về các lượt chuyển đổi lớp quy tắc và các lượt chuyển đổi thuộc tính, bao gồm cả sự khác biệt chi tiết về các tuỳ chọn trước và sau khi chuyển đổi. Chế độ LITE
sẽ xuất ra cùng một thông tin mà không có sự khác biệt về tuỳ chọn.
Đầu ra thông báo giao thức
--output=proto
Tuỳ chọn này sẽ in các mục tiêu thu được ở dạng vùng đệm giao thức nhị phân. Bạn có thể xem định nghĩa về vùng đệm giao thức tại src/main/protobuf/analysis.proto.
CqueryResult
là thông báo cấp cao nhất chứa kết quả của truy vấn. Tệp này có một danh sách các thông báo ConfiguredTarget
và một danh sách các thông báo Configuration
. Mỗi ConfiguredTarget
có một configuration_id
có giá trị bằng 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 trả về thông tin cấu hình trong 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ề kết quả biểu đồ của query
để 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 tạo ra khớp với truy vấn tương tự như danh sách được in ở cuối lệnh gọi bazel build
. Kết quả 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 như cờ --output_groups
xác định.
Tệp này bao gồm các tệp nguồn.
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 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 của cquery khác với tệp BUILD hoặc .bzl. Thư viện này bao gồm tất cả các hằng số và hàm tích hợp sẵn của 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
, đồng thời không hỗ trợ câu lệnh tải.
build_options(target)
build_options(target)
trả về một bản đồ có khoá là giá trị nhận dạng tuỳ chọn bản dựng (xem phần Cấu hình) và giá trị là giá trị Starlark. 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 khỏi bản đồ này.
Nếu mục tiêu là tệp đầu vào, build_options(target)
sẽ trả về None, vì mục tiêu tệp đầu vào có cấu hình rỗng.
nhà cung cấp(mục tiêu)
providers(target)
trả về một tệp ánh xạ có khoá là tên của nhà cung cấp (ví dụ: "DefaultInfo"
) và có giá trị là giá trị Starlark. Các trì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 tên cơ sở được phân tách bằng dấu cách gồm tất cả các tệp do //foo
tạo:
bazel cquery //foo --output=starlark \ --starlark:expr="' '.join([f.basename for f in target.files.to_list()])"
In danh sách các đường dẫn được phân tách bằng dấu cách của tất cả tệp do các mục tiêu rule tạo ra trong //bar
và các gói con của tệp đó:
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ả 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 đầu ra biên dịch được 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 tạo //foo
.
bazel cquery //foo --output=starlark \ --starlark:expr="build_options(target)['//command_line_option:javacopt']"
In nhãn của từng mục tiêu bằng đúng một dữ liệu đầ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 từng mục tiêu 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 mộ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à xuất sắc trong các lĩnh vực khác nhau. Hãy xem xét những điều sau để quyết định xem công cụ nào phù hợp với bạn:
cquery
theo các nhánhselect()
cụ thể để lập mô hình chính xác biểu đồ mà bạn tạo.query
không biết bản dựng chọn nhánh nào, vì vậy, sẽ ước tính quá mức bằng cách đưa tất cả các nhánh vào.- Độ chính xác của
cquery
yêu cầu phải tạo nhiều biểu đồ hơn so vớiquery
. Cụ thể,cquery
đánh giá các mục tiêu đã định cấu hình trong khiquery
chỉ đánh giá các mục tiêu. Việc này sẽ mất nhiều thời gian 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ẽ gây ra sự mơ hồ màquery
tránh được. Ví dụ: nếu"//foo"
tồn tại trong hai cấu hình, thìcquery "deps(//foo)"
sẽ sử dụng cấu hình nào? Hàm[config](#config)
có thể giúp bạn làm việc này. - Là một công cụ mới hơn,
cquery
thiếu khả năng hỗ trợ 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ả mục tiêu mà cquery
"tạo" phải có cùng cấu hình.
Trước khi đánh giá truy vấn, cquery
sẽ kích hoạt một bản dựng chỉ trước khi các thao tác bản dựng thực thi. Theo mặc định, các mục tiêu mà lớp này "tạo" được chọn từ tất cả các nhãn xuất hiện trong biểu thức truy vấn (có thể ghi đè giá trị này 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 quy tắc 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 của riêng chúng bằng các chuyển đổi cạnh sắp tới.
Đây là điểm thiếu sót của cquery
.
Giải pháp: Nếu có thể, hãy đặt --universe_scope
ở 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 đó, do đó, dễ thu thập kết quả từ các truy vấn trước đó. Ví dụ: genquery
thực hiện quá trình chuyển đổi máy chủ lưu trữ trên thuộc tính tools
của nó – tức là định cấu hình các công cụ của nó trong cấu hình máy chủ lưu trữ.
Bạn có thể thấy hiệu ứng còn lạ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 (host_config) ... $ bazel cquery "//foo:tool" tool(host_config)
Giải pháp: thay đổi bất kỳ tuỳ chọn khởi động nào để 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 này. Điều này là do các hạn chế về 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ụ: do một số mục tiêu trong //foo/...
không thể tạo bằng cờ bản dựng đã chọn), hãy mở gói mẫu theo cách thủ công thành các gói thành phần bằng truy vấn xử lý trước:
# 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 "+" -))"