پرس و جو قابل تنظیم (کوئری)

cquery گونه‌ای از query است که به‌درستی اثرات select() و build گزینه‌ها را بر روی نمودار ساخت مدیریت می‌کند.

این امر با اجرا کردن نتایج مرحله تحلیل Bazel که این اثرات را یکپارچه می کند، به دست می آورد. query ، بر اساس کنتراست، قبل از ارزیابی گزینه ها، روی نتایج مرحله بارگیری Bazel اجرا می شود.

مثلا:

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

هر نتیجه شامل یک شناسه منحصر به فرد (9f87702) از پیکربندی هدف است.

از آنجایی که cquery روی گراف هدف پیکربندی شده اجرا می شود. بینشی در مورد مصنوعاتی مانند اقدامات ساخت و یا دسترسی به [test_suite](/reference/be/general#test_suite) زیرا آنها اهداف پیکربندی شده نیستند. برای مورد اول، به [aquery](/docs/aquery) کنید.

نحو پایه

یک تماس cquery ساده به نظر می رسد:

bazel cquery "function(//target)"

عبارت پرس و جو "function(//target)" شامل موارد زیر است:

  • function(...) تابعی است که روی هدف اجرا می شود. cquery بیشتر توابع query را پشتیبانی می کند، به علاوه چند مورد جدید.
  • //target عبارتی است که به تابع داده می شود. در این مثال، عبارت یک هدف ساده است. اما زبان پرس و جو همچنین اجازه تودرتو کردن توابع را می دهد. برای مثال به Query How-To مراجعه کنید.

cquery به هدف نیاز دارد تا در مراحل بارگذاری و تحلیل اجرا شود. مگر اینکه غیر از این مشخص شده باشد، cquery هدف(های) فهرست شده در عبارت query را تجزیه می کند. برای پرس و جو وابستگی اهداف ساخت سطح بالا به --universe_scope مراجعه کنید.

پیکربندی

خط:

//tree:ash (9f87702)

یعنی //tree:ash در پیکربندی با شناسه 9f87702 ساخته شده است. برای اکثر اهداف، این یک هش مات از مقادیر گزینه ساخت است که پیکربندی را تعریف می کند.

برای مشاهده محتوای کامل پیکربندی، اجرا کنید:

$ bazel config 9f87702

پیکربندی میزبان از شناسه ویژه (HOST) استفاده می کند. فایل‌های منبع تولید نشده، مانند مواردی که معمولاً در srcs یافت می‌شوند، از شناسه ویژه (null) استفاده می‌کنند (زیرا نیازی به پیکربندی ندارند).

9f87702 پیشوند شناسه کامل است. این به این دلیل است که شناسه های کامل هش SHA-256 هستند که طولانی و سخت است. cquery هر پیشوند معتبر یک شناسه کامل، مشابه هش کوتاه Git را می‌فهمد. برای مشاهده شناسه های کامل، $ bazel config را اجرا کنید.

ارزیابی الگوی هدف

//foo برای cquery معنای متفاوتی نسبت به query دارد. این به این دلیل است که cquery اهداف پیکربندی شده را ارزیابی می کند و نمودار ساخت ممکن است چندین نسخه پیکربندی شده //foo داشته باشد.

برای cquery ، یک الگوی هدف در عبارت query برای هر هدف پیکربندی شده با برچسبی که با آن الگو مطابقت دارد ارزیابی می‌شود. خروجی قطعی است، اما cquery هیچ تضمینی برای سفارشی فراتر از قرارداد سفارش پرس و جو اصلی ایجاد نمی کند.

این کار نتایج ظریف تری را برای عبارات پرس و جو نسبت به query ایجاد می کند. به عنوان مثال، موارد زیر می توانند نتایج متعددی ایجاد کنند:

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

اگر می‌خواهید دقیقاً مشخص کنید که کدام نمونه باید پرس و جو شود، از تابع config استفاده کنید.

برای اطلاعات بیشتر در مورد الگوهای هدف به مستندات الگوی هدف query مراجعه کنید.

کارکرد

از مجموعه توابع پشتیبانی شده توسط query , cquery از همه به جز visible , siblings , buildfiles های ساخت و tests ها پشتیبانی می کند .

cquery همچنین توابع جدید زیر را معرفی می کند:

پیکربندی

expr ::= config(expr, word)

اپراتور config تلاش می کند تا هدف پیکربندی شده برای برچسب مشخص شده با آرگومان اول و پیکربندی مشخص شده توسط آرگومان دوم را پیدا کند.

مقادیر معتبر برای آرگومان دوم عبارتند از target , host , null , یا یک هش پیکربندی سفارشی . هش ها را می توان از $ bazel config bazel یا خروجی cquery کرد.

مثال ها:

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

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

اگر همه نتایج آرگومان اول را نمی توان در پیکربندی مشخص شده پیدا کرد، فقط آنهایی که می توانند پیدا شوند برگردانده می شوند. اگر هیچ نتیجه ای در پیکربندی مشخص شده یافت نشد، پرس و جو با شکست مواجه می شود.

گزینه ها

گزینه های ساخت

cquery روی یک بیلد معمولی Bazel اجرا می شود و بنابراین مجموعه گزینه های موجود در طول ساخت را به ارث می برد.

استفاده از گزینه های جستجو

--universe_scope (لیست جدا شده با کاما)

اغلب، وابستگی های اهداف پیکربندی شده از طریق انتقال می گذرد، که باعث می شود پیکربندی آنها با وابسته به آنها متفاوت باشد. این پرچم به شما امکان می دهد یک هدف را جویا شوید که گویی به عنوان یک وابستگی یا وابستگی گذرا از یک هدف دیگر ساخته شده است. مثلا:

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

Genrules ابزارهای خود را در پیکربندی میزبان پیکربندی می‌کنند تا پرس و جوهای زیر خروجی‌های زیر را تولید کنند:

پرس و جو هدف ساخته شده است خروجی
bazel cquery "//x:tool" //x:ابزار //x:tool(targetconfig)
bazel cquery "//x:tool" --universe_scope="//x:my_gen" //x:my_gen //x:tool(hostconfig)

اگر این پرچم تنظیم شود، محتوای آن ساخته می شود. اگر تنظیم نشده باشد، همه اهداف ذکر شده در عبارت query به جای آن ساخته می شوند. بسته شدن گذرا از اهداف ساخته شده به عنوان جهان پرس و جو استفاده می شود. در هر صورت، اهدافی که قرار است ساخته شوند باید در سطح بالا قابل ساخت باشند (یعنی سازگار با گزینه های سطح بالا). بازده cquery منجر به بسته شدن موقت این اهداف سطح بالا می شود.

حتی اگر امکان ساخت تمام اهداف در یک عبارت پرس و جو در سطح بالا وجود داشته باشد، ممکن است این کار را انجام ندهید. به عنوان مثال، تنظیم صریح --universe_scope می تواند از ساخت چندین بار اهداف در پیکربندی هایی که برایتان مهم نیست جلوگیری کند. همچنین می‌تواند به تعیین نسخه پیکربندی هدفی که به دنبال آن هستید کمک کند (زیرا در حال حاضر نمی‌توان آن را به روش دیگری به طور کامل مشخص کرد). اگر عبارت کوئری شما پیچیده تر از deps(//foo) باشد، باید این پرچم را تنظیم کنید.

--implicit_deps (بولی، پیش‌فرض=True)

تنظیم این پرچم روی false تمام نتایجی را که به صراحت در فایل BUILD تنظیم نشده اند و در جای دیگری توسط Bazel تنظیم شده اند، فیلتر می کند. این شامل فیلتر کردن زنجیره های ابزار حل شده است.

--tool_deps (بولی، پیش‌فرض=True)

تنظیم این پرچم روی false تمام اهداف پیکربندی شده را که مسیر هدف مورد نظر به آنها از یک انتقال بین پیکربندی هدف و پیکربندی های غیر هدف عبور می کند، فیلتر می کند. اگر هدف مورد نظر در پیکربندی هدف باشد، تنظیم --notool_deps فقط اهدافی را که در پیکربندی هدف نیز هستند برمی گرداند. اگر هدف مورد نظر در یک پیکربندی غیرهدف باشد، تنظیم --notool_deps فقط اهداف را در پیکربندی‌های غیرهدف نیز برمی‌گرداند. این تنظیم معمولاً بر فیلتر زنجیره ابزارهای حل شده تأثیر نمی گذارد.

--include_aspects (بولی، پیش‌فرض=True)

جنبه‌ها می‌توانند وابستگی‌های اضافی را به یک ساخت اضافه کنند. به‌طور پیش‌فرض، cquery از جنبه‌ها پیروی نمی‌کند، زیرا آن‌ها نمودار قابل پرس‌وجو را بزرگ‌تر می‌کنند، که از حافظه بیشتری استفاده می‌کند. اما پیروی از آنها نتایج دقیق تری ایجاد می کند.

اگر نگران تأثیر حافظه پرس و جوهای بزرگ نیستید، این پرچم را به طور پیش فرض در bazelrc خود فعال کنید.

اگر پرس و جو را با جنبه های غیرفعال انجام دهید، می توانید مشکلی را تجربه کنید که در آن هدف X در هنگام ساختن هدف Y با شکست مواجه می شود، اما در cquery somepath(Y, X) و cquery deps(Y) | grep 'X' هیچ نتیجه ای بر نمی گرداند زیرا وابستگی از طریق یک جنبه رخ می دهد.

فرمت های خروجی

به‌طور پیش‌فرض، خروجی‌های cquery به فهرستی از جفت‌های برچسب و پیکربندی مرتب شده بر اساس وابستگی منجر می‌شوند. گزینه های دیگری نیز برای افشای نتایج وجود دارد.

انتقال ها

--transitions=lite
--transitions=full

انتقال پیکربندی برای ساخت اهداف در زیر اهداف سطح بالا در پیکربندی های متفاوت از اهداف سطح بالا استفاده می شود.

به عنوان مثال، یک هدف ممکن است یک انتقال به پیکربندی میزبان را بر روی تمام وابستگی‌ها در ویژگی tools خود اعمال کند. اینها به عنوان انتقال ویژگی شناخته می شوند. قوانین همچنین می‌توانند انتقال‌هایی را بر پیکربندی‌های خود اعمال کنند که به عنوان انتقال کلاس قانون شناخته می‌شوند. این فرمت خروجی اطلاعاتی در مورد این انتقال‌ها مانند نوع آنها و تأثیری که بر گزینه‌های ساخت دارند را خروجی می‌دهد.

این فرمت خروجی توسط پرچم --transitions می شود که به طور پیش فرض روی NONE تنظیم شده است. می توان آن را روی حالت FULL یا LITE تنظیم کرد. حالت FULL اطلاعات مربوط به انتقال کلاس قانون و انتقال صفت از جمله تفاوت دقیق گزینه ها قبل و بعد از انتقال را به بیرون می دهد. حالت LITE همان اطلاعات را بدون تفاوت گزینه ها خروجی می دهد.

خروجی پیام پروتکل

--output=proto

این گزینه باعث می شود که اهداف به دست آمده در یک فرم بافر پروتکل باینری چاپ شوند. تعریف بافر پروتکل را می توان در src/main/protobuf/analysis.proto یافت.

CqueryResult پیام سطح بالایی است که حاوی نتایج جستجو است. دارای فهرستی از پیام‌های ConfiguredTarget و فهرستی از پیام‌های Configuration است. هر ConfiguredTarget یک configuration_id دارد که مقدار آن برابر با فیلد id از پیام Configuration مربوطه است.

--[no]proto:include_configurations

به طور پیش فرض، نتایج cquery اطلاعات پیکربندی را به عنوان بخشی از هر هدف پیکربندی شده برمی گرداند. اگر می خواهید این اطلاعات را حذف کنید و خروجی پروتویی را دریافت کنید که دقیقاً مانند خروجی پروتو query فرمت شده است، این پرچم را روی false تنظیم کنید.

برای گزینه های بیشتر مربوط به خروجی پروتو، به مستندات خروجی پروتو پرس و جو مراجعه کنید.

خروجی نمودار

--output=graph

این گزینه خروجی را به عنوان یک فایل .dot سازگار با Graphviz تولید می کند. برای جزئیات به مستندات خروجی نمودار query مراجعه کنید. cquery از --graph:node_limit و --graph:factored نیز پشتیبانی می کند.

خروجی فایل ها

--output=files

این گزینه لیستی از فایل های خروجی تولید شده توسط هر هدف را چاپ می کند که با پرس و جو مشابه لیست چاپ شده در انتهای فراخوان bazel build مطابقت دارد. خروجی فقط حاوی فایل‌هایی است که در گروه‌های خروجی درخواستی تبلیغ می‌شوند، همانطور که توسط پرچم --output_groups تعیین می‌شود و هرگز حاوی فایل‌های منبع نیست.

تعریف فرمت خروجی با استفاده از Starlark

--output=starlark

این فرمت خروجی یک تابع Starlark را برای هر هدف پیکربندی شده در نتیجه پرس و جو فراخوانی می کند و مقدار بازگردانده شده توسط تماس را چاپ می کند. پرچم --starlark:file مکان یک فایل Starlark را مشخص می کند که تابعی به نام format با یک پارامتر منفرد، target تعریف می کند. این تابع برای هر هدف در نتیجه پرس و جو فراخوانی می شود. به‌علاوه، برای راحتی، می‌توانید فقط بدنه تابعی را که به‌عنوان def format(target): return expr با استفاده از پرچم --starlark:expr .

لهجه استارلارک 'کوئری'

محیط cquery Starlark با یک فایل BUILD یا .bzl متفاوت است. این شامل تمام ثابت‌ها و توابع داخلی Starlark است، به‌علاوه چند مورد خاص cquery که در زیر توضیح داده شده است، اما نه (به عنوان مثال) glob ، native یا rule ، و از گزاره‌های بار پشتیبانی نمی‌کند.

build_options (هدف)

build_options(target) نقشه‌ای را برمی‌گرداند که کلیدهای آن شناسه‌های گزینه ساخت هستند (به تنظیمات مراجعه کنید) و مقادیر آن مقادیر Starlark آنها هستند. گزینه های ساخت که مقادیر آن ها قانونی نیست، مقادیر Starlark از این نقشه حذف شده اند.

اگر هدف یک فایل ورودی باشد، build_options(target) None را برمی‌گرداند، زیرا هدف‌های فایل ورودی دارای پیکربندی تهی هستند.

ارائه دهندگان (هدف)

providers(target) نقشه ای را برمی گرداند که کلیدهای آن نام ارائه دهندگان است (مثلاً "DefaultInfo" ) و مقادیر آن مقادیر Starlark آنها هستند. ارائه دهندگانی که مقادیر آنها ارزش Starlark قانونی نیست از این نقشه حذف شده است.

مثال ها

چاپ یک لیست با فاصله از نام های پایه همه فایل های تولید شده توسط //foo :

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

یک لیست جدا شده با فاصله از مسیرهای همه فایل های تولید شده توسط اهداف قانون در //bar و زیر بسته های آن چاپ کنید:

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

فهرستی از یادگارهای تمام اقدامات ثبت شده توسط //foo را چاپ کنید.

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

فهرستی از خروجی های کامپایل ثبت شده توسط cc_library //baz را چاپ کنید.

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

هنگام ساخت //foo مقدار گزینه خط فرمان --javacopt را چاپ کنید.

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

برچسب هر هدف را دقیقاً با یک خروجی چاپ کنید. این مثال از توابع Starlark تعریف شده در یک فایل استفاده می کند.

  $ 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

برچسب هر هدف را که کاملاً پایتون 3 است چاپ کنید. این مثال از توابع Starlark تعریف شده در یک فایل استفاده می کند.

  $ 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

یک مقدار را از یک ارائه دهنده تعریف شده توسط کاربر استخراج کنید.

  $ 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 در مقابل پرس و جو

cquery و query مکمل یکدیگر هستند و در جایگاه های مختلف برتری می یابند. موارد زیر را در نظر بگیرید تا تصمیم بگیرید کدام یک برای شما مناسب است:

  • cquery از شاخه های select() خاصی پیروی می کند تا نمودار دقیقی را که می سازید مدل کند. query نمی داند که ساخت کدام شاخه را انتخاب می کند، بنابراین با گنجاندن همه شاخه ها بیش از حد تقریب می یابد.
  • دقت cquery نیاز به ساخت بیشتر نمودار نسبت به query دارد. به طور خاص، cquery اهداف پیکربندی شده را ارزیابی می کند در حالی که query فقط اهداف را ارزیابی می کند. این کار زمان بیشتری می برد و حافظه بیشتری مصرف می کند.
  • cquery از زبان پرس و جو ابهامی را ایجاد می کند که query از آن جلوگیری می کند. به عنوان مثال، اگر "//foo" در دو پیکربندی وجود داشته باشد، از کدام یک باید cquery "deps(//foo)" استفاده شود؟ تابع [config](#config) می تواند در این مورد کمک کند.
  • به عنوان یک ابزار جدیدتر، cquery فاقد پشتیبانی از موارد استفاده خاص است. برای جزئیات بیشتر به مسائل شناخته شده مراجعه کنید.

مشکلات شناخته شده

تمام اهدافی که cquery "می سازد" باید پیکربندی یکسانی داشته باشند.

قبل از ارزیابی کوئری‌ها، cquery یک build تا درست قبل از نقطه‌ای که عملیات ساخت اجرا می‌شود، راه‌اندازی می‌کند. اهدافی که "ساخت" می کند به طور پیش فرض از همه برچسب هایی که در عبارت query ظاهر می شوند انتخاب می شوند (این را می توان با --universe_scope ). اینها باید تنظیمات یکسانی داشته باشند.

در حالی که اینها معمولاً پیکربندی "هدف" سطح بالا را به اشتراک می گذارند، قوانین می توانند پیکربندی خود را با انتقال لبه های ورودی تغییر دهند. اینجاست که cquery کوتاه می آید.

راه حل: در صورت امکان، --universe_scope را روی یک محدوده دقیق تر تنظیم کنید. مثلا:

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

خروجی غیر قطعی

cquery به طور خودکار نمودار ساخت را از دستورات قبلی پاک نمی کند و بنابراین مستعد برداشتن نتایج از پرس و جوهای گذشته است. به عنوان مثال، genquery یک انتقال میزبان را روی ویژگی tools خود اعمال می کند - یعنی ابزارهای خود را در پیکربندی میزبان پیکربندی می کند.

در زیر می توانید اثرات ماندگار آن انتقال را مشاهده کنید.

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

راه‌حل: هر گزینه راه‌اندازی را برای تحلیل مجدد اهداف پیکربندی شده تغییر دهید. برای مثال، --test_arg=&lt;whatever&gt; به دستور ساخت شما

عیب یابی

الگوهای هدف بازگشتی ( /... )

اگر با:

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

این به اشتباه نشان می‌دهد بسته //foo در محدوده نیست حتی اگر --universe_scope=//foo:app شامل آن باشد. این به دلیل محدودیت های طراحی در cquery است. به عنوان یک راه حل، به صراحت //foo/... را در محدوده جهان قرار دهید:

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

اگر کار نکرد (مثلاً، چون برخی از اهداف در //foo/... نمی‌توانند با پرچم‌های ساخت انتخاب‌شده بسازند)، به‌طور دستی الگو را در بسته‌های تشکیل‌دهنده‌اش با یک جستجوی پیش پردازش باز کنید:

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