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 các tuỳ chọn tạo trên biểu đồ bản dựng.
Nền tảng này làm được điều này bằng cách chạy trên các kết quả giai đoạn phân tích của Bazel, trong đó tích hợp các hiệu ứng này. Ngược lại, query
chạy trên kết quả giai đoạn tải của Bazel trước khi đánh giá các tuỳ chọn.
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 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ư quyền truy cập vào các quy tắc test_suite
vì chúng không phải là mục tiêu được định cấu hình. Để xem trước, hãy xem aquery
.
Cú pháp cơ bản
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:
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ố chức năng mới.//target
là biểu thức được 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. 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
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
để biết 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ích hợp 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à hàm băm 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 mã:
$ bazel config 9f87702
9f87702
là tiền tố của mã nhận dạng đầy đủ. Nguyên nhân 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ột mã nhận dạng hoàn chỉnh, tương tự như Hàm băm ngắn trên Git.
Để xem các mã nhận dạng hoàn chỉnh, hãy chạy $ bazel config
.
Đánh giá mẫu mục tiêu
//foo
mang ý 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 được định cấu hình và biểu đồ bản dựng có thể có nhiều phiên bản //foo
được định cấu hình.
Đối với cquery
, mẫu mục tiêu trong biểu thức truy vấn đánh giá mọi mục tiêu đã định cấu hình có nhãn khớp với mẫu đó. Đầu ra là xác định, nhưng cquery
không đảm bảo thứ tự ngoài hợp đồng thứ tự truy vấn chính.
Điều này tạo ra kết quả tinh vi hơn cho biểu thức truy vấn so với query
.
Ví dụ: các lệnh sau có thể cho 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ể nào cần truy vấn qua đó, hãy 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ề các mẫu mục tiêu.
Hàm
Trong số tập hợp hàm mà query
hỗ trợ, cquery
hỗ trợ tất cả ngoại trừ visible
, siblings
, buildfiles
, và tests
.
cquery
cũng ra mắt 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.
Giá trị hợp lệ cho đối số thứ hai là null
hoặc một 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 kết quả của một 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 thể tìm thấy mọi kết quả của đối số đầu tiên trong cấu hình đã chỉ định, thì hệ thống sẽ chỉ trả về các kết quả có thể tìm thấy. 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
cquery
chạy trên một bản dựng Bazel thông thường nên kế thừa tập hợp tuỳ chọn có sẵn trong quá trình tạo 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 được đị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 các mục tiêu đó 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_binary( name = "tool", srcs = ["tool.cpp"], )
Quy tắc tạo sinh định cấu hình các công cụ của chúng trong cấu hình thực thi để các truy vấn sau đây sẽ tạo ra các 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(execconfig) |
Nếu bạn đặt cờ này, nội dung của cờ sẽ được tạo. Nếu bạn không đặt giá trị này, thì tất cả các mục tiêu đã đề cập trong biểu thức truy vấn sẽ được tạo. Trạng thái đóng bắc cầu của các mục tiêu đã tạo đượ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 tạo phải có thể tạo được ở 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ả sẽ đóng bắc cầu các mục tiêu cấp cao nhất này.
Ngay cả khi có thể tạo 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 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 việc tạo mục tiêu nhiều lần trong các cấu hình mà bạn không quan tâm. Thao tác này cũng có thể giúp chỉ định phiên bản cấu hình của một mục tiêu bạn đang tìm kiếm (vì hiện tại bạn không thể chỉ định đầy đủ việc nà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, mặc định=True)
Việc đặt cờ này thành false sẽ lọc mọi kết quả chưa được đặt rõ ràng trong tệp BUILD và thay vào đó được đặt ở nơi khác do Bazel. Điều này bao gồm cả việc lọc các chuỗi công 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 đó sẽ 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 có 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 chuỗi công cụ đã được phân giải.
--include_aspects
(boolean, mặc định=True)
Bao gồm các phần phụ thuộc được thêm theo khía cạnh.
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 nó 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ó những lựa chọn khác để hiển thị kết quả.
Kiểu chuyển cảnh
--transitions=lite --transitions=full
Các lượt chuyển đổi cấu hình được dùng để tạo mục tiêu bên dưới mục tiêu cấp cao nhất trong các cấu hình khác với mục tiêu cấp cao nhất.
Ví dụ: mục tiêu có thể áp dụng chuyển đổi cho cấu hình thực thi trên tất cả các phần phụ thuộc trong thuộc tính tools
. Những hoạt động này được gọi là chuyển đổi thuộc tính. Các quy tắc cũng có thể áp dụng quá trình chuyển đổi đối với cấu hình của riêng chúng,
được gọi là chuyển đổi lớp quy tắc. Định dạng đầu ra này sẽ đưa ra thông tin về các hiệu ứng chuyển đổi này, chẳng hạn như loại chuyển đổi và tác động của chúng đối với các tuỳ chọn xây dựng.
Định dạng đầu ra này được kích hoạt bởi cờ --transitions
được đặt thành NONE
theo mặc định. Bạn có thể đặt chế độ này ở chế độ FULL
hoặc LITE
. Chế độ FULL
sẽ cung cấp thông tin về quá trình chuyển đổi lớp quy tắc và 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 khi chuyển đổi. Chế độ LITE
sẽ đưa ra cùng thông tin mà không có sự khác biệt về tuỳ chọn.
Thông báo về giao thức
--output=proto
Tuỳ chọn này khiến các mục tiêu thu được sẽ được in dưới 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_v2.proto.
CqueryResult
là thông báo cấp cao nhất chứa kết quả của truy vấn. Lớp này có một danh sách thông báo ConfiguredTarget
và một danh sách 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 từng mục tiêu được đị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 chính xác như đầu ra proto của truy vấn, hãy thiết lập cờ này thành false.
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.
Kết quả của 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ả của 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 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 do cờ --output_groups
xác định.
Có bao gồm các tệp nguồn.
Tất cả đường dẫn do định dạng đầu ra này phát ra đều tương ứng với execroot mà bạn có thể lấy thông qua bazel info execution_root
. Nếu tồn tại một đường liên kết tượng trưng tiện lợi bazel-out
, thì các đường dẫn đến các tệp trong kho lưu trữ chính cũng sẽ 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 từng 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 hàm có tên format
bằng một tham số duy nhất là target
. Hàm này được gọi cho mỗi Target (Mục tiêu) trong kết quả truy vấn. Ngoài ra, để thuận tiện, bạn có thể chỉ chỉ định phần 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ữ "cquery" Starlark
Môi trường cquery Starlark khác với tệp BUILD hoặc .bzl. Giao diệ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ùng với một số hằng số và hàm dành riêng cho truy vấn được mô tả dưới đây, nhưng không hỗ trợ (ví dụ) glob
, native
hoặc rule
, đồng thời 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ó khoá là các giá trị nhận dạng tuỳ chọn bản dựng (xem Cấu hình) và 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ị hợp pháp của Starlark sẽ bị loại khỏi bản đồ này.
Nếu mục tiêu là tệp đầu vào, build_options(target)
sẽ trả về Không có, vì các 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 bản đồ có khoá là tên của nhà cung cấp (ví dụ: "DefaultInfo"
) và 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 pháp sẽ bị loại 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 tên cơ sở của tất 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 một 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ả 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 thông tin ghi nhớ 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 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 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 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 từng 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 một giá trị từ Provider 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ổ trợ cho nhau và nổi bật trong những lĩnh vực khác nhau. Hãy cân nhắc những yếu tố sau để quyết định xem cách nào phù hợp với bạn:
cquery
tuân theo các nhánhselect()
cụ thể để lập mô hình biểu đồ chính xác mà bạn tạo.query
không biết bản dựng chọn nhánh nào, vì vậy, hãy ước tính bằng cách thêm tất cả các nhánh.- Để đảm bảo độ chính xác của
cquery
, biểu đồ cần được xây dựng nhiều hơn so vớiquery
. Cụ thể,cquery
đánh giá mục tiêu đã định cấu hình, cònquery
chỉ đánh giá mục tiêu. Quá trình này mất nhiều thời gian hơn và tốn nhiều bộ nhớ hơn. - Cách diễn giải của
cquery
về ngôn ngữ truy vấn gây ra một sự không rõ ràng màquery
tránh được. Ví dụ: nếu"//foo"
tồn tại trong hai cấu hình, thìcquery "deps(//foo)"
nên sử dụng cấu hình nào? Hàmconfig
có thể giúp bạn làm việc này. - Là một công cụ mới hơn,
cquery
không hỗ trợ một số trường hợp sử dụng nhất định. Vui lòng 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
"tạo" phải có cùng một cấu hình.
Trước khi đánh giá truy vấn, cquery
sẽ kích hoạt một bản dựng ngay trước thời điểm thực thi các hành động tạo bản dựng. Theo mặc định, các mục tiêu mà nó "tạo ra" được chọn từ tất cả các nhãn xuất hiện trong biểu thức truy vấn (điều này có thể được 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 quy tắc này thường có chung 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 chính chúng bằng chuyển đổi cạnh tới.
Đây là lúc cquery
thiếu.
Giải pháp: Nếu có thể, hãy đặt --universe_scope
ở 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ả 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 đó, bạn có thể nhận kết quả từ các truy vấn trước đây. Ví dụ: genrule
thực hiện quá trình chuyển đổi exec trên thuộc tính tools
của nó – tức là nó định cấu hình các công cụ của nó trong cấu hình execor.
Bạn có thể xem các tác độ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 tạo.
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 gói //foo
không thuộc phạm vi của gói mặc dù --universe_scope=//foo:app
có bao gồm gói đó. Điều này là do các giới hạn 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ụ: vì một số mục tiêu trong //foo/...
không thể tạo bản dựng bằng các 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 cấu thành của nó bằng một 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 "+" -))"