Yapılandırılabilir Sorgu (cquery)

Sorun bildirin Kaynağı göster

cquery, query varyantının select() ve derleme seçeneklerinin derleme grafiğindeki etkilerini doğru şekilde işleyen bir varyantıdır.

Bunun için Bazel'in bu efektleri entegre eden analiz aşamasının sonuçlarını gözden geçirir. Buna karşılık query, seçenekler değerlendirilmeden önce Bazel'ın yükleme aşamasının sonuçları üzerinden ilerler.

Örneğin:

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

Her sonuç, hedefin oluşturulduğu yapılandırmanın benzersiz bir tanımlayıcısını (9f87702) içerir.

cquery, yapılandırılmış hedef grafiğin üzerinde çalıştığı için yapılandırılmış hedef olmadıkları için derleme işlemleri veya test_suite kurallarına erişim gibi yapılar hakkında analiz sağlamaz. Birincisi için aquery sayfasını inceleyin.

Temel söz dizimi

Basit bir cquery çağrısı şu şekilde görünür:

bazel cquery "function(//target)"

"function(//target)" sorgu ifadesi şunlardan oluşur:

  • function(...), hedefte çalıştırılacak işlevdir. cquery, query işlevlerinin çoğunu ve birkaç yenisini destekler.
  • //target, işleve aktarılan ifadedir. Bu örnekte, ifade basit bir hedeftir. Ancak sorgu dili, işlevlerin iç içe yerleştirilmesine de olanak tanır. Örnekler için Sorgu rehberine bakın.

cquery, bir hedefin yükleme ve analiz aşamalarından geçmesini gerektirir. Aksi belirtilmedikçe cquery, sorgu ifadesinde listelenen hedefleri ayrıştırır. Üst düzey derleme hedeflerinin bağımlılıklarını sorgulamak için --universe_scope bölümüne bakın.

Yapılandırmalar

Satır:

//tree:ash (9f87702)

//tree:ash ürününün, 9f87702 kimliğine sahip bir yapılandırmada oluşturulduğu anlamına gelir. Çoğu hedef için bu, yapılandırmayı tanımlayan derleme seçeneği değerlerinin opak bir karmasıdır.

Yapılandırmanın tüm içeriğini görmek için şu komutu çalıştırın:

$ bazel config 9f87702

9f87702, tam kimliğin ön ekidir. Bunun nedeni, tam kimliklerin uzun ve takip edilmesi zor SHA-256 karmaları olmasıdır. cquery, Git kısa karmalarına benzer şekilde, tam bir kimliğin geçerli tüm ön eklerini anlar. Kimliklerin tamamını görmek için $ bazel config komutunu çalıştırın.

Hedef kalıp değerlendirmesi

//foo, cquery için query dilinden farklı bir anlama sahiptir. Bunun nedeni, cquery özelliğinin yapılandırılmış hedefleri değerlendirmesi ve derleme grafiğinde //foo öğesinin birden fazla yapılandırılmış sürümüne sahip olabilmesidir.

cquery için sorgu ifadesindeki hedef kalıp, her yapılandırılmış hedefi o kalıpla eşleşen bir etiketle değerlendirir. Çıkış belirleyicidir ancak cquery, temel sorgu sipariş sözleşmesi dışında hiçbir sipariş garantisi vermez.

Bu, sorgu ifadeleri için query ile karşılaştırıldığında daha ince sonuçlar üretir. Örneğin, aşağıdakiler birden fazla sonuç üretebilir:

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

Hangi örneğin üzerinde sorgu yapılacağını kesin olarak belirtmek istiyorsanız config işlevini kullanın.

Hedef kalıpları hakkında daha fazla bilgi için query hedef kalıbı belgelerine bakın.

İşlevler

cquery, query tarafından desteklenen işlev grubundan visible, siblings, buildfiles ve tests hariç tüm işlevleri destekler.

cquery aşağıdaki yeni işlevleri de kullanıma sunuyor:

config

expr ::= config(expr, word)

config operatörü, ilk bağımsız değişken ve ikinci bağımsız değişken tarafından belirtilen yapılandırma ile belirtilen etiket için yapılandırılmış hedefi bulmaya çalışır.

İkinci bağımsız değişken için geçerli değerler null veya özel yapılandırma karmasıdır. Karmalar, $ bazel config veya önceki bir cquery çıkışından alınabilir.

Örnekler:

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

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

Belirtilen yapılandırmada ilk bağımsız değişkenin tüm sonuçları bulunamazsa yalnızca bulunabilen sonuçlar döndürülür. Belirtilen yapılandırmada herhangi bir sonuç bulunamazsa sorgu başarısız olur.

Seçenekler

Derleme seçenekleri

cquery, normal bir Bazel derlemesi üzerinde çalışır ve bu nedenle derleme sırasında kullanılabilen seçenekler kümesini devralır.

Sorgu seçeneklerini kullanma

--universe_scope (virgülle ayrılmış liste)

Yapılandırılmış hedeflerin bağımlılıkları genellikle geçişlerden geçer. Bu da yapılandırmalarının bağımlılarından farklı olmasına neden olur. Bu işaret, bir hedefi başka bir hedefin bağımlılık veya geçişli bağımlılığı olarak oluşturulmuş gibi sorgulamanıza olanak tanır. Örneğin:

# 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, araçlarını exec yapılandırmasında yapılandırır. Böylece aşağıdaki sorgular şu çıkışları üretir:

Sorgu Hedef Oluşturuldu Çıkış
bazel cquery "//x:tool" //x:tool //x:tool(targetconfig)
bazel cquery "//x:tool" --universe_scope="x:my_gen" //x:my_gen //x:tool(execconfig)

Bu işaret ayarlanırsa içeriği oluşturulur. Ayarlanmazsa sorgu ifadesinde belirtilen tüm hedefler oluşturulur. Oluşturulan hedeflerin geçişli olarak kapatılması, sorgunun evreni olarak kullanılır. Her iki durumda da, oluşturulacak hedefler en üst düzeyde derlenebilir (yani üst düzey seçeneklerle uyumlu) olmalıdır. cquery sonuçları, bu üst düzey hedeflerin geçişli olarak kapatılmasıyla sonuçlanır.

Bir sorgu ifadesindeki tüm hedefleri en üst düzeyde oluşturmak mümkün olsa bile bunu yapmamak faydalı olabilir. Örneğin, --universe_scope politikasını açık bir şekilde ayarlamak, önemsemediğiniz yapılandırmalarda hedefleri birden çok kez oluşturmayı önleyebilir. Aradığınız hedefin yapılandırma sürümünü belirtmenize de yardımcı olabilir (çünkü şu anda bunu başka bir şekilde tam olarak belirtmek mümkün değildir). Sorgu ifadeniz deps(//foo) ifadesinden daha karmaşıksa bu işareti ayarlamanız gerekir.

--implicit_deps (boole, varsayılan=Doğru)

Bu işareti false (yanlış) olarak ayarlamak, BUILD dosyasında açıkça ayarlanıp Bazel tarafından başka bir yere ayarlanmayan tüm sonuçları filtreler. Buna çözümlenmiş araç zincirlerinin filtrelenmesi de dahildir.

--tool_deps (boole, varsayılan=Doğru)

Bu işaret yanlış olarak ayarlanırsa sorgulanan hedeften kendilerine giden yolun, hedef yapılandırması ile hedef olmayan yapılandırmalar arasındaki geçişten geçtiği tüm yapılandırılmış hedefler filtrelenir. Sorgulanan hedef hedef yapılandırmadaysa --notool_deps politikasının ayarlanması, yalnızca hedef yapılandırmada bulunan hedefleri döndürür. Sorgulanan hedef, hedef olmayan bir yapılandırmadaysa --notool_deps ayarlanması, yalnızca hedef olmayan yapılandırmalardaki hedefleri de döndürür. Bu ayar genellikle çözümlenen araç zincirlerinin filtrelenmesini etkilemez.

--include_aspects (boole, varsayılan=Doğru)

Yönler ile eklenen bağımlılıkları dahil edin.

Bu işaret devre dışı bırakılırsa X yalnızca bir yönden bağımlıysa cquery somepath(X, Y) ve cquery deps(X) | grep 'Y' Y değerini atlar.

Çıkış biçimleri

Varsayılan olarak Cquery çıkışları, bağımlılık sıralı bir etiket ve yapılandırma çifti listesiyle sonuçlanır. Sonuçları göstermek için başka seçenekler de vardır.

Geçişler

--transitions=lite
--transitions=full

Yapılandırma geçişleri, üst düzey hedeflerden farklı yapılandırmalarda üst düzey hedeflerin altında hedefler oluşturmak için kullanılır.

Örneğin, bir hedef, tools özelliğindeki tüm bağımlılıklara yönetici yapılandırmasına geçiş uygulayabilir. Bunlar, özellik geçişleri olarak bilinir. Kurallar, kural sınıfı geçişleri olarak bilinen kendi yapılandırmalarına da geçişler uygulayabilir. Bu çıkış biçimi, geçişlerin türleri ve derleme seçenekleri üzerindeki etkileri gibi geçişlerle ilgili bilgiler verir.

Bu çıkış biçimi, varsayılan olarak NONE değerine ayarlanmış --transitions işareti tarafından tetiklenir. FULL veya LITE moduna ayarlanabilir. FULL modu, geçişten önceki ve sonraki seçeneklerin ayrıntılı farklılıkları dahil olmak üzere kural sınıfı geçişleri ve özellik geçişleri hakkında bilgi verir. LITE modu, farklı seçenekler olmadan aynı bilgileri verir.

Protokol mesajı çıkışı

--output=proto

Bu seçenek, sonuçta elde edilen hedeflerin ikili protokol arabelleği biçiminde yazdırılmasına neden olur. Protokol arabelleğinin tanımını src/main/protobuf/analysis_v2.proto adresinde bulabilirsiniz.

CqueryResult, sorgu sonuçlarını içeren üst düzey mesajdır. ConfiguredTarget mesaj ve Configuration mesajdan oluşan bir liste içerir. Her ConfiguredTarget, değeri ilgili Configuration mesajındaki id alanının değerine eşit olan bir configuration_id içerir.

--[no]proto:include_configurations

Varsayılan olarak Cquery sonuçları, yapılandırılmış her hedefin bir parçası olarak yapılandırma bilgilerini döndürür. Bu bilgiyi atlamak ve tam olarak sorgunun proto çıkışıyla aynı şekilde biçimlendirilmiş bir proto çıkışı almak istiyorsanız bu işareti false (yanlış) olarak ayarlayın.

Proto çıkışıyla ilgili daha fazla seçenek için sorgunun proto çıkışı belgelerine bakın.

Grafik çıkışı

--output=graph

Bu seçenek, çıktıyı Graphviz uyumlu bir .dot dosyası olarak oluşturur. Ayrıntılı bilgi için query grafik çıkışı dokümanlarına bakın. cquery, --graph:node_limit ve --graph:factored özelliklerini de destekler.

Dosya çıkışı

--output=files

Bu seçenek, bazel build çağrısının sonunda yazdırılan listeye benzer şekilde, sorguyla eşleşen her hedef tarafından üretilen çıkış dosyalarının bir listesini yazdırır. Çıkış, yalnızca --output_groups işaretiyle belirlenen istenen çıkış gruplarında tanıtılan dosyaları içerir. Kaynak dosyalar bu kapsama girer.

Bu çıkış biçimi tarafından yayınlanan tüm yollar, bazel info execution_root aracılığıyla elde edilebilen execroot'a göredir. bazel-out kolaylık sembolü varsa ana depodaki dosyaların yolları da çalışma alanı dizinine göre çözümlenir.

Starlark'ı kullanarak çıkış biçimini tanımlama

--output=starlark

Bu çıkış biçimi, sorgu sonucundaki her yapılandırılmış hedef için Starlark işlevini çağırır ve çağrı tarafından döndürülen değeri yazdırır. --starlark:file işareti, tek bir target parametresine sahip format adlı bir işlevi tanımlayan bir Starlark dosyasının konumunu belirtir. Bu işlev, sorgu sonucundaki her bir Hedef için çağrılır. Alternatif olarak, kolaylık sağlaması için --starlark:expr işaretini kullanarak def format(target): return expr olarak tanımlanan bir işlevin gövdesini de belirtebilirsiniz.

"cquery" Starlark lehçesi

Sorgu Starlark ortamı, BUILD veya .bzl dosyasından farklı. Tüm temel Starlark yerleşik sabit değerlerini ve işlevlerini ve aşağıda açıklanan sorguya özgü birkaç sabiti içerir, ancak (örneğin) glob, native veya rule içermez ve yükleme ifadelerini desteklemez.

build_options(target)

build_options(target), anahtarları derleme seçeneği tanımlayıcıları (bkz. Yapılandırmalar) olan ve değerleri Starlark değerleri olan bir haritayı döndürür. Değerleri yasal Starlark değerleri olmayan derleme seçenekleri bu haritadan çıkarılır.

Hedef bir giriş dosyasıysa giriş dosyası hedeflerinin boş yapılandırması olduğundan build_options(target), Yok değerini döndürür.

sağlayıcılar(hedef)

providers(target), anahtarları sağlayıcıların adları (örneğin, "DefaultInfo") ve değerleri Starlark değerleri olan bir eşleme döndürür. Değerleri yasal Starlark değerleri olmayan sağlayıcılar bu haritadan çıkarılır.

Örnekler

//foo tarafından üretilen tüm dosyaların temel adlarının boşlukla ayrılmış listesini yazdır:

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

//bar içindeki ve alt paketlerindeki kural hedefleri tarafından oluşturulan tüm dosyaların yollarının boşlukla ayrılmış listesini yazdırın:

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

//foo tarafından kaydedilen tüm işlemlerin anımsatıcılarının listesini yazdırın.

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

cc_library //baz tarafından kaydedilen derleme çıkışlarının listesini yazdırın.

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

//foo oluştururken --javacopt komut satırı seçeneğinin değerini yazdırın.

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

Her hedefin etiketini tam olarak bir çıkışla yazdırın. Bu örnekte, bir dosyada tanımlanan Starlark işlevleri kullanılmaktadır.

  $ 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

Her hedefin etiketini (tam olarak Python 3) yazdırın. Bu örnekte, bir dosyada tanımlanan Starlark işlevleri kullanılmaktadır.

  $ 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

Kullanıcı tanımlı bir Sağlayıcıdan değer ayıklayın.

  $ 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 ve sorgu

cquery ve query birbirini tamamlayarak farklı nişlerde başarılı oluyor. Sizin için hangisinin uygun olduğuna karar vermek üzere aşağıdakileri göz önünde bulundurun:

  • cquery, oluşturduğunuz grafiği tam olarak modellemek için belirli select() dallarını izler. query, derlemenin hangi dalı seçtiğini bilmediği için, tüm dalları dahil ederek tahminden fazlasını yapar.
  • cquery işlevinin hassasiyeti, query ile kıyaslandığında daha fazla grafik oluşturmayı gerektirir. Özellikle, cquery yapılandırılmış hedefleri, query ise yalnızca hedefleri değerlendirir. Bu işlem daha fazla zaman alır ve daha fazla bellek kullanır.
  • cquery tarafından sorgu dili yorumlanması, query tarafından kaçınılan belirsizliklere yol açmaktadır. Örneğin, "//foo" iki yapılandırmada mevcutsa cquery "deps(//foo)" hangisini kullanmalıdır? config işlevi bu konuda yardımcı olabilir.
  • Daha yeni bir araç olan cquery, belirli kullanım alanları için desteğe sahip değildir. Ayrıntılar için Bilinen sorunlar başlıklı makaleyi inceleyin.

Bilinen sorunlar

cquery "derlediği" tüm hedefler aynı yapılandırmaya sahip olmalıdır.

cquery, sorguları değerlendirmeden önce, derleme işlemlerinin yürütüleceği noktadan hemen önceki bir derlemeyi tetikler. "Oluşturduğu" hedefler, varsayılan olarak sorgu ifadesinde görünen tüm etiketler arasından seçilir (bu işlem --universe_scope ile geçersiz kılınabilir). Bunların aynı yapılandırmaya sahip olması gerekir.

Bunlar genellikle üst düzey "hedef" yapılandırmasını paylaşsa da kurallar, kendi yapılandırmalarını gelen uç geçişlerle değiştirebilir. cquery bu noktada yetersiz kalıyor.

Geçici çözüm: Mümkünse --universe_scope öğesini daha sıkı bir kapsama ayarlayın. Örneğin:

# 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

--output=xml için destek sunulmaz.

Deterministik olmayan çıktı.

cquery, önceki komutlardan derleme grafiğini otomatik olarak silmez ve bu nedenle, geçmiş sorgulardan sonuç almaya eğilimlidir. Örneğin, genrule, tools özelliğinde bir yönetici geçişi uygular. Yani, araçlarını yürütme yapılandırmasındaki yapılandırır.

Bu geçişin süregelen etkilerini aşağıda görebilirsiniz.

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

Geçici çözüm: Yapılandırılmış hedeflerin yeniden analiz edilmesini zorunlu kılmak için tüm başlatma seçeneklerini değiştirin. Örneğin, derleme komutunuza --test_arg=<whatever> komutunu ekleyin.

Sorun giderme

Yinelemeli hedef kalıpları (/...)

Aşağıdaki durumlarla karşılaşırsanız:

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

bu durum, //foo paketinin --universe_scope=//foo:app içerdiği hâlde yanlışlıkla kapsamda olmadığını gösterir. Bunun nedeni, cquery içindeki tasarım sınırlamalarıdır. Geçici bir çözüm olarak, evren kapsamına //foo/... öğesini açıkça ekleyin:

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

Bu çözüm işe yaramazsa (örneğin, //foo/... içindeki bazı hedefler seçilen derleme işaretleriyle derlenemediğinden) bir ön işleme sorgusu kullanarak kalıbı kendi bileşen paketlerine manuel olarak açın:

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