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=<whatever>
به دستور ساخت شما
عیب یابی
الگوهای هدف بازگشتی ( /...
)
اگر با:
$ 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 "+" -))"