طلب قابل للإعداد (cquery)

cquery هي صيغة من query تتعامل بشكلٍ صحيح مع select() وتنشئ خيارات' التأثيرات على الرسم البياني للإصدار.

تحقّق هذه النتيجة من خلال عرض نتائج تحليل في Bazel، حيث تدمج هذه التأثيرات. على النقيض من query، تظهر النتائج في مرحلة تحميل Bazel's قبل أن يتم تقييم الخيارات.

مثلاً:

$ 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's الدوال، بالإضافة إلى عدد قليل منها.
  • //target هو التعبير الذي يتم تطبيقه على الدالة. في هذا المثال، التعبير هدف بسيط. وتتيح لغة طلب البحث أيضًا دمج الدوال. يُرجى الاطّلاع على طريقة إجراء طلب البحث للحصول على أمثلة.

ويتطلب cquery أن يكون الاستهداف من خلال تنفيذ مراحل التحميل والتحليل. وما لم يُحدِّد خلاف ذلك، يحلِّل cquery الهدف(الأهداف) المُدرَجة في تعبير طلب البحث. اطّلِع على الرمز --universe_scope للاستعلام عن تبعيات أهداف الإصدار ذي المستوى الأعلى.

الإعدادات

السطر:

//tree:ash (9f87702)

تعني أنّه تم إنشاء //tree:ash في ضبط برقم التعريف 9f87702. بالنسبة إلى معظم الأهداف، هذه تجزئة غير واضحة لقيم خيار الإصدار التي تحدِّد الإعدادات.

للاطّلاع على المحتوى الذي اكتمل ضبطه، يجب تنفيذ ما يلي:

$ bazel config 9f87702

يستخدم إعداد المضيف رقم التعريف الخاص (HOST). تستخدم الملفات المصدر التي لم يتم إنشاؤها، مثل الملفات المتوفرة عادةً في srcs، رقم التعريف الخاص (null) (لأنه لا يلزم ضبطها).

9f87702 هو بادئة المعرّف الكامل. وذلك لأنّ المعرّفات الكاملة تجزئات SHA-256 طويلة جدًا ويصعب متابعتها. يتعرّف العنصر cquery على أي بادئة صالحة لرقم تعريف كامل، مثل علامات التجزئة القصيرة. للاطّلاع على معرّفات كاملة، عليك تشغيل $ bazel config.

تقييم النمط المستهدف

//foo له معنى cquery مختلف عن query. ويرجع ذلك إلى أن cquery يقيّم الاستهدافات التي تم ضبطها، وقد يحتوي الرسم البياني للإصدار على عدّة إصدارات تم ضبطها من //foo.

بالنسبة إلى cquery، يتم تقييم نمط الاستهداف في تعبير طلب البحث لكل استهداف مُعدّ يتضمن تصنيفًا يطابق ذلك النمط. هذه النتيجة إلزامية ولكن لا تقدّم خدمة 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's وثائق الأنماط المستهدفة لمزيد من المعلومات حول الأنماط المستهدفة.

الدوال

من بين مجموعة الدوال المتوافقة مع query، تتوافق cquery مع الكل باستثناء visible وsiblings وbuildfiles وtests.

تقدِّم cquery أيضًا الوظائف الجديدة التالية:

config

expr ::= config(expr, word)

يحاول عامل التشغيل config العثور على الاستهداف الذي تم ضبطه للتصنيف الذي يشير إليه الوسيطة الأولى والضبط الذي تحدّده الوسيطة الثانية.

القيم الصالحة للوسيطة الثانية هي target أو host أو null أو تجزئة إعداد مخصّص. يمكن استرداد علامات التجزئة من $ bazel config أو مخرجات 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:tool //x:tool(targetconfig)
bazel cquery"//x:tool" --universe_scopeليون؛;//x:my_gen" //x:my_gen //x:tool(hostconfig)

إذا تم ضبط هذه العلامة، يتم إنشاء محتوياتها. وإذا لم يتم ضبطه، يتم إنشاء جميع الأهداف المذكورة في تعبير طلب البحث بدلاً من ذلك. ويتم استخدام الإغلاق العام للأهداف المستهدفة ككون طلب البحث. وفي كلتا الحالتين، يجب أن تكون الأهداف التي يتم إنشاؤها قابلة للإنشاء في المستوى الأعلى (أي أن تكون متوافقة مع خيارات المستوى الأعلى). وتعرض القيمة cquery النتائج الختامية للتصفّح الانتقالي لهذه الأهداف ذات المستوى الأعلى.

حتى إذا كان من الممكن إنشاء جميع الاستهدافات في تعبير طلب البحث على المستوى العلوي، قد يكون من المفيد عدم إجراء ذلك. على سبيل المثال، قد يؤدي تعيين صراحةً إلى --universe_scope إلى منع إنشاء الأهداف عدة مرات في عمليات الضبط التي لا تهتم بها. ويمكن أن يساعد أيضًا في تحديد إصدار تهيئة الاستهداف الذي تبحث عنه (نظرًا لعدم إمكانية تحديد هذا بشكل كامل بأي طريقة أخرى). يجب ضبط هذه العلامة إذا كان تعبير طلب البحث أكثر تعقيدًا من deps(//foo).

--implicit_deps (منطقي، تلقائي = صحيح)

يؤدي ضبط هذه العلامة على "خطأ" إلى استبعاد جميع النتائج التي لم يتم ضبطها بشكل صريح في ملف BUILD، وبدلاً من ذلك تم ضبطها في مكان آخر بواسطة Bazel. ويشمل ذلك فلترة سلاسل الأدوات التي تم حلّها.

--tool_deps (منطقي، تلقائي = صحيح)

ويؤدي ضبط هذه العلامة على "خطأ" إلى استبعاد جميع الاستهدافات التي تم ضبطها لأنّ المسار من الاستهداف الوارد في طلب البحث يؤدي إلى الانتقال بين عملية ضبط الاستهداف وعمليات الضبط غير المستهدفة. إذا كان الهدف الوارد في طلب البحث قيد الضبط، لن يؤدي إعداد --notool_deps إلا إلى عرض الأهداف المتضمّنة أيضًا في الإعدادات المستهدفة. وإذا كان الاستهداف الوارد في طلب البحث في إعداد غير مستهدف، سيؤدي إعداد --notool_deps إلى عرض الأهداف فقط في عمليات الضبط غير المستهدفة. لا يؤثر هذا الإعداد بشكل عام في تصفية سلاسل الأدوات التي تم حلها.

--include_aspects (منطقي، تلقائي = صحيح)

يمكن أن تضيف الجوانب تبعيات إضافية للمبنى. لا يتّبع cquery تلقائيًا جوانب عديدة، لأنّه يزيد من حجم الرسم البياني لطلب البحث، ما يؤدي إلى استغلال مساحة أكبر في الذاكرة. ويؤدي متابعتها إلى الحصول على نتائج أكثر دقة.

إذا لم تكن قلقًا بشأن تأثير الذاكرة على طلبات البحث الكبيرة، فعِّل هذه العلامة تلقائيًا في مخابزك.

إذا كنت تبحث في حال إيقاف بعض الجوانب، يمكن أن تواجه مشكلة حيث يتعذّر الهدف المستهدف 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_configuration

تعرض نتائج cquery تلقائيًا معلومات الضبط كجزء من كل هدف تم ضبطه. إذا كنت تريد حذف هذه المعلومات والحصول على نتائج مبدئية تم تنسيقها تمامًا مثل التي يأتي بها طلب البحث، عليك ضبط هذه العلامة على "خطأ".

اطّلِع على مستندات مخرجات طلب البحث في طلبات البحث والطلبات لمعرفة المزيد من الخيارات المتعلّقة بالنتائج الأولية.

مخرجات الرسم البياني

--output=graph

ينشئ هذا الخيار نتائج كملف بتنسيق .dot متوافق مع Graphviz. راجع query'sوثائق إخراج الرسم البياني للحصول على التفاصيل. cquery تتيح أيضًا استخدام --graph:node_limit و--graph:factored.

مخرجات الملفات

--output=files

يطبع هذا الخيار قائمة بملفات الإخراج التي تم إنشاؤها بواسطة كل هدف مطابق عبر طلب البحث المماثل للقائمة المطبوعة في نهاية استدعاء bazel build. لا تتضمّن المخرجات إلا الملفات المُعلَن عنها في مجموعات النتائج المطلوبة كما هو محدّد من خلال علامة --output_groups، ولا تتضمن أبدًا ملفات المصدر.

تعريف تنسيق الناتج باستخدام Starlark

--output=starlark

يستدعي تنسيق الناتج هذا دالة Starlark لكل استهداف تم إعداده في نتيجة طلب البحث، كما يطبع القيمة التي تعرضها المكالمة. تحدّد العلامة --starlark:file موقع ملف Starlark الذي يحدّد دالة باسم format باستخدام معلّمة واحدة، وهي target. ويتم استدعاء هذه الدالة لكل Target في نتيجة طلب البحث. بدلاً من ذلك، يمكنك تحديد نص الدالة المحدّدة على أنّها def format(target): return expr باستخدام العلامة --starlark:expr فقط.

'cquery' لهجة Starlark

تختلف بيئة cquery Starlark عن ملف BUILD أو .bzl. ويشمل كل الثوابت والدوال المُدمَجة في Starlark، بالإضافة إلى بعض الثوابت والدوال المُخصَّصة لطلب البحث كما هو موضَّح أدناه، على سبيل المثال لا الحصر glob أو native أو rule، ولا يتوافق مع بيانات التحميل.

Build_options(target)

تعرض build_options(target) خريطة تضم معرّفات خيارات الإصدار (يُرجى الاطّلاع على عمليات الضبط) وقيمها هي قيم Starlark. يتم حذف خيارات الإصدار التي لا تكون قيمها قيم Starlark القانونية من هذه الخريطة.

إذا كان الهدف هو ملف إدخال، تعرض build_options(target) القيمة "بدون"، لأن أهداف ملف الإدخال لها إعداد فارغ.

موفّرو الخدمة(الهدف)

تعرض 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()]"

اطبع قيمة خيار سطر الأوامر --javacopt عند إنشاء //foo.

  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

اطبع تصنيف كل هدف وهو Python 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–39;s إنشاء رسم بياني أكبر من query. على وجه التحديد، cquery يقيّم الاستهدافات التي تم إعدادها بينما query يقيّم الأهداف فقط. وهذا يستغرق وقتًا أطول ويستخدم المزيد من الذاكرة.
  • cquery's تفسير لغة طلب البحث لغموض تجنب query. على سبيل المثال، إذا تم العثور على "//foo" في ضبطين، أيهما يجب استخدام cquery "deps(//foo)"؟ يمكن أن تساعد دالة [config](#config) في ذلك.
  • كأداة أحدث، تفتقر cquery إلى الدعم لبعض حالات الاستخدام. يُرجى الاطِّلاع على المشاكل المعروفة للحصول على التفاصيل.

المشاكل المعروفة

يجب أن يكون لدى جميع الأهداف التي cquery "builds" الإعدادات نفسها.

قبل تقييم طلبات البحث، يشغّل cquery إصدارًا يصل إلى المرحلة التي تسبق تنفيذ إجراءات الإصدار مباشرةً. وتكون الأهداف "builds" مُحدَّدة تلقائيًا في جميع التصنيفات التي تظهر في تعبير طلب البحث (يمكن إلغاء هذه العملية باستخدام --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 "+" -))"