Kueri yang Dapat Dikonfigurasi (cquery)

Tetap teratur dengan koleksi Simpan dan kategorikan konten berdasarkan preferensi Anda.
Laporkan masalah Lihat sumber

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

Alat ini mencapainya dengan menjalankan hasil fase analisis Bazel, yang mengintegrasikan efek ini. Sebaliknya, query 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 menyertakan ID unik (9f87702) dari konfigurasi target yang dibuat.

Karena cquery berjalan di atas grafik target yang dikonfigurasi, grafik tidak memiliki insight tentang artefak seperti tindakan build atau akses ke aturan [test_suite](/reference/be/general#test_suite) karena keduanya bukan target yang dikonfigurasi. Untuk yang pertama, lihat [aquery](/docs/aquery).

Sintaksis dasar

Panggilan cquery sederhana akan terlihat seperti:

bazel cquery "function(//target)"

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

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

cquery memerlukan target untuk dijalankan melalui fase pemuatan dan analisis. Kecuali ditentukan lain, cquery akan mengurai target yang tercantum dalam ekspresi kueri. Lihat --universe_scope untuk membuat kueri dependensi target build level atas.

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 konten konfigurasi yang lengkap, jalankan:

$ bazel config 9f87702

Konfigurasi host menggunakan ID khusus (HOST). File sumber yang tidak dibuat, seperti yang biasa ditemukan di srcs, menggunakan ID khusus (null) (karena tidak perlu dikonfigurasi).

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

Evaluasi pola target

//foo memiliki arti yang berbeda untuk cquery dibandingkan dengan 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 menjamin jaminan di luar kontrak pengurutan kueri inti.

Tindakan ini akan menghasilkan hasil yang lebih halus untuk ekspresi kueri dibandingkan dengan query. Misalnya, hasil berikut dapat memberikan beberapa hasil:

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

Jika Anda ingin mendeklarasikan instance mana yang akan digunakan untuk kueri, gunakan fungsi config.

Lihat dokumentasi pola target query untuk informasi selengkapnya tentang pola target.

Functions

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

cquery juga memperkenalkan fungsi baru berikut:

config

expr ::= config(expr, word)

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

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

Contoh:

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

$ 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 ditemukan dalam konfigurasi yang ditentukan, kueri akan gagal.

Opsi

Opsi build

cquery berjalan pada build Bazel biasa sehingga mewarisi kumpulan opsi yang tersedia selama proses build.

Menggunakan opsi cquery

--universe_scope (daftar yang dipisahkan koma)

Sering kali, dependensi target yang dikonfigurasi akan melalui transisi, yang menyebabkan konfigurasinya berbeda dari dependensinya. Flag ini memungkinkan Anda membuat kueri target seolah-olah dibuat sebagai dependensi atau dependensi transitif target lain. Contoh:

# x/BUILD
genrule(
     name = "my_gen",
     srcs = ["x.in"],
     outs = ["x.cc"],
     cmd = "$(locations :tool) $< >$@",
     tools = [":tool"],
)
cc_library(
    name = "tool",
)

Genrules mengonfigurasi alatnya dalam konfigurasi host sehingga kueri berikut akan menghasilkan output berikut:

Kueri Target Dibangun Output
kueri bazel "//x:tool" //x:tool //x:tool(targetconfig)
kueri bazel "//x:tool" --universe_scope"}x:my_gen" //x:my_gen //x:tool(hostconfig)

Jika tanda ini ditetapkan, kontennya akan dibuat. Jika tidak ditetapkan, semua target yang disebutkan dalam ekspresi kueri akan dibuat. Penutupan transitif target yang dibuat digunakan sebagai lingkup kueri. Apa pun itu, target yang akan dibuat harus dapat di-build di level teratas (yaitu, kompatibel dengan opsi level atas). cquery menampilkan hasil transitif penutupan target level atas ini.

Meskipun Anda dapat mem-build semua target dalam ekspresi kueri di tingkat atas, sebaiknya jangan lakukan hal tersebut. Misalnya, menetapkan --universe_scope secara eksplisit dapat mencegah build target beberapa kali dalam konfigurasi yang tidak penting bagi Anda. Hal ini juga dapat membantu menentukan versi konfigurasi target yang Anda cari (karena saat ini tidak mungkin untuk menentukannya sepenuhnya dengan cara lain). Anda harus menetapkan tanda ini jika ekspresi kueri lebih kompleks daripada deps(//foo).

--implicit_deps (boolean, default=True)

Menyetel tanda ini ke false akan memfilter semua hasil yang tidak disetel secara eksplisit dalam file BUILD dan sebaliknya ditetapkan di tempat lain oleh Bazel. Hal ini termasuk memfilter toolchain yang telah diselesaikan.

--tool_deps (boolean, default=True)

Jika tanda ini disetel ke salah (false), semua target yang dikonfigurasi dan jalur dari target yang dikueri akan dialihkan ke konfigurasi transisi antara konfigurasi target dan konfigurasi non-target. Jika target yang dikueri ada dalam konfigurasi target, menetapkan --notool_deps hanya akan menampilkan target yang juga ada dalam konfigurasi target. Jika target yang dikueri berada dalam konfigurasi non-target, menetapkan --notool_deps hanya akan menampilkan target juga dalam konfigurasi non-target. Setelan ini umumnya tidak memengaruhi pemfilteran toolchain yang di-resolve.

--include_aspects (boolean, default=True)

Aspek dapat menambahkan dependensi tambahan ke build. Secara default, cquery tidak mengikuti aspek karena membuat grafik yang dapat dikueri lebih besar, yang menggunakan lebih banyak memori. Namun mengikutinya akan memberikan hasil yang lebih akurat.

Jika tidak khawatir dengan dampak memori dari kueri yang besar, aktifkan flag ini secara default di bazelrc Anda.

Jika membuat kueri dengan aspek yang dinonaktifkan, Anda dapat mengalami masalah ketika target X gagal saat membuat target Y, tetapi cquery somepath(Y, X) dan cquery deps(Y) | grep 'X' tidak menampilkan hasil karena dependensi terjadi melalui aspek.

Format output

Secara default, output kueri menghasilkan daftar pasangan label dan konfigurasi yang diurutkan dependensi. Ada juga opsi lain untuk mengekspos hasilnya.

Transisi

--transitions=lite
--transitions=full

Transisi konfigurasi digunakan untuk membuat target di bawah target tingkat atas dalam konfigurasi yang berbeda dari target tingkat atas.

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

Format output ini dipicu oleh flag --transitions yang secara default ditetapkan ke NONE. Mode ini dapat disetel ke mode FULL atau LITE. Mode FULL menghasilkan informasi tentang transisi class aturan dan transisi atribut, termasuk perbedaan detail opsi sebelum dan sesudah transisi. Mode LITE menampilkan informasi yang sama tanpa perbedaan opsi.

Output pesan protokol

--output=proto

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

CqueryResult adalah pesan tingkat atas yang berisi hasil kueri. Ini memiliki daftar pesan ConfiguredTarget dan daftar pesan Configuration. Setiap ConfiguredTarget memiliki configuration_id yang nilainya sama dengan kolom id dari pesan Configuration yang sesuai.

--[no]proto:sertakan_konfigurasi

Secara default, hasil kueri 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.

Lihat dokumentasi output proto kueri untuk mengetahui 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 di grup output yang diminta sebagaimana ditentukan oleh flag --output_groups. File ini mencakup file sumber.

Menentukan format output menggunakan Starlark

--output=starlark

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

Dialek Starlark 'cquery'

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

build_options(target)

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

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

penyedia(target)

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

Contoh

Cetak daftar nama file yang dihasilkan oleh //foo yang dipisahkan dengan spasi:

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

Cetak daftar jalur yang dipisahkan spasi dari semua file yang dihasilkan oleh target aturan dalam //bar dan subpaketnya:

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

Cetak daftar mnemonik semua tindakan yang terdaftar oleh //foo.

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

Cetak 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()]"

Cetak 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(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

Cetak label setiap target yang benar-benar 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

kueri vs. kueri

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

  • cquery mengikuti cabang select() tertentu untuk memodelkan grafik persis yang Anda buat. query tidak mengetahui cabang yang dipilih build, sehingga mendekati dengan menyertakan semua cabang.
  • Ketepatan cquery mengharuskan pembuatan lebih banyak grafik daripada query. Secara khusus, cquery mengevaluasi target yang dikonfigurasi sementara query hanya mengevaluasi target. Tindakan ini memerlukan lebih banyak waktu dan menggunakan lebih banyak memori.
  • Integritas cquery bahasa kueri memperkenalkan ambiguitas yang dihindari query. Misalnya, jika "//foo" ada dalam dua konfigurasi, mana yang harus digunakan oleh cquery "deps(//foo)"? Fungsi [config](#config) dapat membantu menangani hal ini.
  • Sebagai alat yang lebih baru, cquery tidak memiliki dukungan untuk kasus penggunaan tertentu. Lihat Masalah umum untuk mengetahui detailnya.

Masalah umum

Semua target yang "di-build" cquery harus memiliki konfigurasi yang sama.

Sebelum mengevaluasi kueri, cquery memicu build hingga tepat sebelum titik tindakan build akan dijalankan. Target yang "dibuat" secara default dipilih dari semua label yang muncul dalam ekspresi kueri (dapat diganti dengan --universe_scope). Target ini harus memiliki konfigurasi yang sama.

Meskipun aturan ini secara umum memiliki konfigurasi "target" tingkat atas, aturan dapat mengubah konfigurasinya sendiri dengan transisi edge masuk. Di sinilah cquery pendek.

Solusi: Jika memungkinkan, tetapkan --universe_scope ke cakupan yang lebih ketat. Contoh:

# 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

Tidak ada dukungan untuk --output=xml.

Output non-deterministik.

cquery tidak otomatis menghapus grafik build dari perintah sebelumnya, sehingga rentan untuk mengambil hasil dari kueri sebelumnya. Misalnya, genquery memberikan transisi host pada atribut tools miliknya, yaitu mengonfigurasi alatnya dalam konfigurasi host.

Anda dapat melihat efek yang tersisa dari transisi tersebut di bawah ini.

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

Solusi: ubah opsi startup mana pun untuk memaksa analisis ulang pada target yang dikonfigurasi. Misalnya, tambahkan --test_arg=&lt;whatever&gt; ke perintah build.

Pemecahan masalah

Pola target rekursif (/...)

Jika Anda menemukan:

$ 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 berada dalam cakupan meskipun --universe_scope=//foo:app menyertakannya. Hal ini disebabkan oleh pembatasan desain di cquery. Sebagai solusinya, sertakan //foo/... secara eksplisit dalam cakupan universe:

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

Jika tidak berhasil (misalnya, karena beberapa target dalam //foo/... tidak dapat di-build dengan flag build yang dipilih), ekstrak pola secara manual ke dalam paket konstituennya 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 "+" -))"