Kueri yang Dapat Dikonfigurasi (cquery)

Laporkan masalah Lihat sumber Nightly · 8.3 · 8.2 · 8.1 · 8.0 · 7.6

cquery adalah varian query yang menangani select() dan efek opsi build pada grafik build dengan benar.

Hal ini dicapai dengan menjalankan hasil fase analisisBazel, yang mengintegrasikan efek ini. query, sebaliknya, berjalan di atas hasil fase pemuatan Bazel, sebelum opsi dievaluasi.

Contoh:

$ 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)

Setiap hasil mencakup ID unik (9f87702) dari konfigurasi yang digunakan untuk membuat target.

Karena cquery berjalan di atas grafik target yang dikonfigurasi, maka tidak memiliki insight tentang artefak seperti tindakan build maupun akses ke aturan test_suite karena bukan target yang dikonfigurasi. Untuk yang pertama, lihat aquery.

Sintaksis dasar

Panggilan cquery sederhana terlihat seperti:

bazel cquery "function(//target)"

Ekspresi kueri "function(//target)" terdiri dari hal berikut:

  • function(...) adalah fungsi yang akan dijalankan pada target. cquery mendukung sebagian besar fungsi query, serta beberapa fungsi baru.
  • //target adalah ekspresi yang dimasukkan ke fungsi. Dalam contoh ini, ekspresinya adalah target sederhana. Namun, bahasa kueri juga memungkinkan penyusunan fungsi. Lihat Panduan kueri untuk contoh.

cquery memerlukan target untuk menjalankan fase pemuatan dan analisis. Kecuali jika ditentukan lain, cquery akan mengurai target yang tercantum dalam ekspresi kueri. Lihat --universe_scope untuk mengkueri dependensi target build tingkat teratas.

Konfigurasi

Baris berikut:

//tree:ash (9f87702)

berarti //tree:ash dibuat dalam konfigurasi dengan ID 9f87702. Untuk sebagian besar target, ini adalah hash buram dari nilai opsi build yang menentukan konfigurasi.

Untuk melihat seluruh isi konfigurasi, jalankan:

$ bazel config 9f87702

9f87702 adalah awalan ID lengkap. Hal ini karena ID lengkap adalah hash SHA-256, yang panjang dan sulit diikuti. cquery memahami awalan ID lengkap yang valid, mirip dengan hash pendek Git. Untuk melihat ID lengkap, jalankan $ bazel config.

Evaluasi pola target

//foo memiliki arti yang berbeda untuk cquery dan query. Hal ini karena cquery mengevaluasi target yang dikonfigurasi dan grafik build mungkin memiliki beberapa versi //foo yang dikonfigurasi.

Untuk cquery, pola target dalam ekspresi kueri dievaluasi ke setiap target yang dikonfigurasi dengan label yang cocok dengan pola tersebut. Output bersifat deterministik, tetapi cquery tidak memberikan jaminan pengurutan di luar kontrak pengurutan kueri inti.

Hal ini menghasilkan hasil yang lebih halus untuk ekspresi kueri daripada dengan query. Misalnya, berikut ini dapat menghasilkan beberapa hasil:

# 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)

Jika Anda ingin menyatakan secara tepat instance mana yang akan dikueri, gunakan fungsi config.

Lihat dokumentasi pola target query's untuk mengetahui informasi selengkapnya tentang pola target.

Fungsi

Dari kumpulan fungsi yang didukung oleh query, cquery mendukung semua fungsi kecuali allrdeps, buildfiles, rbuildfiles, siblings, tests, dan visible.

cquery juga memperkenalkan fungsi baru berikut:

config

expr ::= config(expr, word)

Operator config mencoba menemukan target yang dikonfigurasi untuk label yang ditunjukkan oleh argumen pertama dan konfigurasi yang ditentukan oleh argumen kedua.

Nilai yang valid untuk argumen kedua adalah null atau hash konfigurasi kustom. Hash dapat diambil dari $ bazel config atau output cquery sebelumnya.

Contoh:

$ bazel cquery "config(//bar, 3732cc8)" --universe_scope=//foo
$ bazel cquery "deps(//foo)"
//bar (exec)
//baz (exec)

$ bazel cquery "config(//baz, 3732cc8)"

Jika tidak semua hasil argumen pertama dapat ditemukan dalam konfigurasi yang ditentukan, hanya hasil yang dapat ditemukan yang akan ditampilkan. Jika tidak ada hasil yang dapat ditemukan dalam konfigurasi yang ditentukan, kueri akan gagal.

Opsi

Opsi build

cquery berjalan melalui build Bazel reguler dan dengan demikian mewarisi serangkaian opsi yang tersedia selama build.

Menggunakan opsi cquery

--universe_scope (daftar yang dipisahkan koma)

Sering kali, dependensi target yang dikonfigurasi mengalami transisi, yang menyebabkan konfigurasinya berbeda dari dependensinya. Flag ini memungkinkan Anda mengkueri target seolah-olah target tersebut dibangun sebagai dependensi atau dependensi transitif dari target lain. Contoh:

# 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 mengonfigurasi alatnya di konfigurasi exec sehingga kueri berikut akan menghasilkan output berikut:

Kueri Target Tercapai Output
bazel cquery "//x:tool" //x:tool //x:tool(targetconfig)
bazel cquery "//x:tool" --universe_scope="//x:my_gen" //x:my_gen //x:tool(execconfig)

Jika tanda ini disetel, kontennya akan dibuat. Jika tidak ditetapkan, semua target yang disebutkan dalam ekspresi kueri akan dibuat. Penutupan transitif target yang dibuat digunakan sebagai semesta kueri. Bagaimanapun juga, target yang akan dibuat harus dapat dibuat di tingkat teratas (yaitu, kompatibel dengan opsi tingkat teratas). cquery menampilkan hasil dalam penutupan transitif dari target tingkat teratas ini.

Meskipun semua target dapat dibuat dalam ekspresi kueri di tingkat teratas, sebaiknya jangan melakukannya. Misalnya, menetapkan --universe_scope secara eksplisit dapat mencegah pembuatan target beberapa kali dalam konfigurasi yang tidak Anda inginkan. Hal ini juga dapat membantu menentukan versi konfigurasi target yang Anda cari. Anda harus menetapkan tanda ini jika ekspresi kueri Anda lebih kompleks daripada deps(//foo).

--implicit_deps (boolean, default=True)

Menetapkan flag ini ke salah (false) akan memfilter semua hasil yang tidak ditetapkan secara eksplisit dalam file BUILD dan ditetapkan di tempat lain oleh Bazel. Hal ini mencakup pemfilteran toolchain yang telah diselesaikan.

--tool_deps (boolean, default=True)

Menetapkan tanda ini ke salah (false) akan memfilter semua target yang dikonfigurasi yang jalur dari target yang dikueri ke target tersebut melintasi transisi antara konfigurasi target dan konfigurasi non-target. Jika target yang dikueri ada dalam konfigurasi target, menyetel --notool_deps hanya akan menampilkan target yang juga ada dalam konfigurasi target. Jika target yang dikueri berada dalam konfigurasi non-target, setelan --notool_deps hanya akan menampilkan target yang juga berada dalam konfigurasi non-target. Setelan ini umumnya tidak memengaruhi pemfilteran toolchain yang telah diselesaikan.

--include_aspects (boolean, default=True)

Sertakan dependensi yang ditambahkan oleh aspek.

Jika tanda ini dinonaktifkan, cquery somepath(X, Y) dan cquery deps(X) | grep 'Y' akan menghapus Y jika X hanya bergantung padanya melalui aspek.

Format keluaran

Secara default, cquery menampilkan hasil dalam daftar pasangan label dan konfigurasi yang diurutkan berdasarkan dependensi. Ada juga opsi lain untuk mengekspos hasilnya.

Transisi

--transitions=lite
--transitions=full

Transisi konfigurasi digunakan untuk membuat target di bawah target tingkat teratas dalam konfigurasi yang berbeda dengan target tingkat teratas.

Misalnya, target dapat memaksakan transisi ke konfigurasi exec pada semua dependensi dalam atribut tools-nya. Hal ini dikenal sebagai transisi atribut. Aturan juga dapat menerapkan transisi pada konfigurasinya sendiri, yang dikenal sebagai transisi class aturan. Format output ini menampilkan informasi tentang transisi ini, seperti jenisnya dan pengaruhnya terhadap opsi build.

Format output ini dipicu oleh tanda --transitions yang secara default disetel ke NONE. Setelan ini dapat diatur ke mode FULL atau LITE. Output mode FULL informasi tentang transisi kelas aturan dan transisi atribut, termasuk perbedaan mendetail opsi sebelum dan setelah transisi. Mode LITE menampilkan informasi yang sama tanpa perbedaan opsi.

Output pesan protokol

--output=proto

Opsi ini menyebabkan target yang dihasilkan dicetak dalam bentuk buffer protokol biner. Definisi buffer protokol dapat ditemukan di src/main/protobuf/analysis_v2.proto.

CqueryResult adalah pesan tingkat teratas yang berisi hasil cquery. Objek ini memiliki daftar pesan ConfiguredTarget dan daftar pesan Configuration. Setiap ConfiguredTarget memiliki configuration_id yang nilainya sama dengan nilai kolom id dari pesan Configuration yang sesuai.

--[no]proto:include_configurations

Secara default, hasil cquery menampilkan informasi konfigurasi sebagai bagian dari setiap target yang dikonfigurasi. Jika Anda ingin menghilangkan informasi ini dan mendapatkan output proto yang diformat persis seperti output proto kueri, setel flag ini ke salah (false).

Lihat dokumentasi output proto kueri untuk opsi terkait output proto lainnya.

Output grafik

--output=graph

Opsi ini menghasilkan output sebagai file .dot yang kompatibel dengan Graphviz. Lihat dokumentasi output grafik query untuk mengetahui detailnya. cquery juga mendukung --graph:node_limit dan --graph:factored.

Output file

--output=files

Opsi ini mencetak daftar file output yang dihasilkan oleh setiap target yang cocok dengan kueri yang mirip dengan daftar yang dicetak di akhir pemanggilan bazel build. Output hanya berisi file yang diiklankan dalam grup output yang diminta sebagaimana ditentukan oleh tanda --output_groups. Paket ini menyertakan file sumber.

Semua jalur yang dikeluarkan oleh format output ini bersifat relatif terhadap execroot, yang dapat diperoleh melalui bazel info execution_root. Jika symlink praktis bazel-out ada, jalur ke file di repositori utama juga diselesaikan relatif terhadap direktori ruang kerja.

Menentukan format output menggunakan Starlark

--output=starlark

Format output ini memanggil fungsi Starlark untuk setiap target yang dikonfigurasi dalam hasil kueri, dan mencetak nilai yang ditampilkan oleh panggilan. Flag --starlark:file menentukan lokasi file Starlark yang menentukan fungsi bernama format dengan satu parameter, target. Fungsi ini dipanggil untuk setiap Target dalam hasil kueri. Atau, untuk mempermudah, Anda dapat menentukan hanya isi fungsi yang dideklarasikan sebagai def format(target): return expr dengan menggunakan flag --starlark:expr.

Dialek Starlark 'cquery'

Lingkungan Starlark cquery berbeda dengan file BUILD atau .bzl. Ini mencakup semua konstanta dan fungsi bawaan Starlark inti, ditambah beberapa konstanta dan fungsi khusus cquery yang dijelaskan di bawah, tetapi tidak (misalnya) glob, native, atau rule, dan tidak mendukung pernyataan load.

build_options(target)

build_options(target) menampilkan peta yang kuncinya adalah ID opsi build (lihat Konfigurasi) dan nilainya adalah nilai Starlark-nya. Opsi build yang nilainya bukan nilai Starlark yang valid akan dihilangkan dari peta ini.

Jika target adalah file input, build_options(target) akan menampilkan None, karena target file input memiliki konfigurasi null.

penyedia(target)

providers(target) menampilkan peta yang kuncinya adalah nama penyedia (misalnya, "DefaultInfo") dan nilainya adalah nilai Starlark-nya. Penyedia yang nilainya bukan nilai Starlark yang sah akan dihilangkan dari peta ini.

Contoh

Mencetak daftar nama dasar semua file yang dihasilkan oleh //foo yang dipisahkan spasi:

  bazel cquery //foo --output=starlark \
    --starlark:expr="' '.join([f.basename for f in providers(target)['DefaultInfo'].files.to_list()])"

Mencetak daftar jalur semua file yang dihasilkan oleh target rule di //bar dan subpaketnya yang dipisahkan dengan spasi:

  bazel cquery 'kind(rule, //bar/...)' --output=starlark \
    --starlark:expr="' '.join([f.path for f in providers(target)['DefaultInfo'].files.to_list()])"

Mencetak daftar mnemonik semua tindakan yang didaftarkan oleh //foo.

  bazel cquery //foo --output=starlark \
    --starlark:expr="[a.mnemonic for a in target.actions]"

Mencetak daftar output kompilasi yang didaftarkan oleh cc_library //baz.

  bazel cquery //baz --output=starlark \
    --starlark:expr="[f.path for f in target.output_groups.compilation_outputs.to_list()]"

Mencetak nilai opsi command line --javacopt saat membuat //foo.

  bazel cquery //foo --output=starlark \
    --starlark:expr="build_options(target)['//command_line_option:javacopt']"

Cetak label setiap target dengan tepat satu output. Contoh ini menggunakan fungsi Starlark yang ditentukan dalam file.

  $ cat example.cquery

  def has_one_output(target):
    return len(providers(target)["DefaultInfo"].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

Cetak label setiap target yang sepenuhnya Python 3. Contoh ini menggunakan fungsi Starlark yang ditentukan dalam file.

  $ 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

Mengekstrak nilai dari Penyedia yang ditentukan pengguna.

  $ 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 vs. query

cquery dan query saling melengkapi dan unggul dalam niche yang berbeda. Pertimbangkan hal berikut untuk memutuskan mana yang tepat untuk Anda:

  • cquery mengikuti cabang select() tertentu untuk membuat model grafik yang tepat yang Anda buat. query tidak tahu cabang mana yang dipilih build, sehingga melakukan overaproksimasi dengan menyertakan semua cabang.
  • Presisi cquery memerlukan pembuatan lebih banyak grafik daripada query. Secara khusus, cquery mengevaluasi target yang dikonfigurasi, sedangkan query hanya mengevaluasi target. Tindakan ini memerlukan waktu lebih lama dan menggunakan lebih banyak memori.
  • Interpretasi cquery terhadap bahasa kueri menimbulkan ambiguitas yang dihindari oleh query. Misalnya, jika "//foo" ada dalam dua konfigurasi, mana yang harus digunakan cquery "deps(//foo)"? Fungsi config dapat membantu Anda.

Output non-deterministik

cquery tidak otomatis menghapus grafik build dari perintah sebelumnya. Oleh karena itu, fitur ini cenderung mengambil hasil dari kueri sebelumnya.

Misalnya, genrule menerapkan transisi exec pada atribut tools-nya, yaitu mengonfigurasi alatnya dalam konfigurasi exec.

Anda dapat melihat efek berkelanjutan dari transisi tersebut di bawah.

$ cat > foo/BUILD <<

Perilaku ini mungkin diinginkan atau tidak, bergantung pada apa yang Anda coba evaluasi.

Untuk menonaktifkannya, jalankan blaze clean sebelum cquery untuk memastikan grafik analisis yang baru.

Pemecahan masalah

Pola target rekursif (/...)

Jika Anda mengalami:

$ 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.

Hal ini secara keliru menunjukkan bahwa paket //foo tidak dalam cakupan meskipun --universe_scope=//foo:app menyertakannya. Hal ini disebabkan oleh batasan desain di cquery. Sebagai solusinya, sertakan //foo/... secara eksplisit dalam cakupan semesta:

$ bazel cquery --universe_scope=//foo:app,//foo/... "somepath(//foo:app, //foo/...)"

Jika cara tersebut tidak berhasil (misalnya, karena beberapa target di //foo/... tidak dapat di-build dengan tanda build yang dipilih), buka pola secara manual ke dalam paket penyusunnya dengan kueri pra-pemrosesan:

# 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 "+" -))"