तारीख सेव करें: BazelCon 2023, 24 से 25 अक्टूबर तक Google म्यूनिख में होगा! ज़्यादा जानें

मैक्रो

किसी समस्या की शिकायत करें स्रोत देखें

इस पेज में मैक्रो इस्तेमाल करने की बुनियादी बातें बताई गई हैं. साथ ही, इसमें इस्तेमाल के सामान्य उदाहरण, डीबग करना, और कन्वेंशन शामिल हैं.

मैक्रो, BUILD फ़ाइल से कॉल किया जाने वाला एक फ़ंक्शन है, जो नियमों को इंस्टैंशिएट करता है. मैक्रो का इस्तेमाल मुख्य रूप से मौजूदा नियमों और अन्य मैक्रो को एनकैप्सुलेशन करने और कोड का दोबारा इस्तेमाल करने के लिए किया जाता है. लोडिंग फ़ेज़ खत्म होने तक, मैक्रो का इस्तेमाल नहीं किया जा सकता. और बेज़ल को इंस्टैंशिएट किए गए नियमों के सिर्फ़ कंक्रीट सेट दिखते हैं.

इस्तेमाल का तरीका

आम तौर पर, मैक्रो का इस्तेमाल तब होता है, जब आपको किसी नियम का दोबारा इस्तेमाल करना हो.

उदाहरण के लिए, BUILD फ़ाइल में genरूल, //:generator का इस्तेमाल करके एक फ़ाइल जनरेट करता है. इसके लिए, कमांड में some_arg आर्ग्युमेंट को हार्डकोड किया जाता है:

genrule(
    name = "file",
    outs = ["file.txt"],
    cmd = "$(location //:generator) some_arg > $@",
    tools = ["//:generator"],
)

अगर आप अलग-अलग तर्कों के साथ ज़्यादा फ़ाइलें जनरेट करना चाहते हैं, तो हो सकता है कि आप इस कोड को मैक्रो फ़ंक्शन से निकालना चाहें. अब हम मैक्रो file_generator को कॉल करते हैं, जिसमें name और arg पैरामीटर हैं. जनगणना के नियम को इन चीज़ों से बदलें:

load("//path:generator.bzl", "file_generator")

file_generator(
    name = "file",
    arg = "some_arg",
)

file_generator(
    name = "file-two",
    arg = "some_arg_two",
)

file_generator(
    name = "file-three",
    arg = "some_arg_three",
)

यहां, आप //path पैकेज में मौजूद .bzl फ़ाइल से file_generator का चिह्न लोड करते हैं. मैक्रो फ़ंक्शन की जानकारी को किसी अलग .bzl फ़ाइल में रखने पर, आपकी BUILD फ़ाइलें साफ़ तौर पर और जानकारी के तौर पर सेव रहती हैं. .bzl फ़ाइल फ़ोल्डर में, किसी भी पैकेज के लोड हो सकते हैं.

आखिर में, path/generator.bzl में मूल genrule परिभाषा को पैरामीटर में बदलने और पैरामीटर तय करने के लिए, मैक्रो की परिभाषा लिखें:

def file_generator(name, arg, visibility=None):
  native.genrule(
    name = name,
    outs = [name + ".txt"],
    cmd = "$(location //:generator) %s > $@" % arg,
    tools = ["//:generator"],
    visibility = visibility,
  )

आप नियमों का एक साथ समूह बनाने के लिए मैक्रो का इस्तेमाल भी कर सकते हैं. यह उदाहरण चेन वाले जेनरल्स दिखाता है, जहां एक जेंट्रल, पिछले जेनरुल के आउटपुट का इस्तेमाल इनपुट के रूप में करता है:

def chained_genrules(name, visibility=None):
  native.genrule(
    name = name + "-one",
    outs = [name + ".one"],
    cmd = "$(location :tool-one) $@",
    tools = [":tool-one"],
    visibility = ["//visibility:private"],
  )

  native.genrule(
    name = name + "-two",
    srcs = [name + ".one"],
    outs = [name + ".two"],
    cmd = "$(location :tool-two) $< $@",
    tools = [":tool-two"],
    visibility = visibility,
  )

यह उदाहरण सिर्फ़ दूसरी जनरेशन के नियम को विज़िबिलिटी वैल्यू असाइन करता है. इसकी मदद से मैक्रो लेखक, फ़ाइल फ़ोल्डर में शामिल टारगेट किए गए दूसरे नियमों की वजह से, इंटरमीडिएट के नियमों के आउटपुट को छिपा सकते हैं.

मैक्रोज़ विस्तृत करना

जब आप जांच करना चाहें कि मैक्रो क्या करता है, तो विस्तृत फ़ॉर्म देखने के लिए --output=build के साथ query कमांड का उपयोग करें:

$ bazel query --output=build :file
# /absolute/path/test/ext.bzl:42:3
genrule(
  name = "file",
  tools = ["//:generator"],
  outs = ["//test:file.txt"],
  cmd = "$(location //:generator) some_arg > $@",
)

नेटिव नियम लागू करें

नेटिव नियमों (जिन नियमों को load() स्टेटमेंट की ज़रूरत नहीं होती है) को नेटिव मॉड्यूल से इंस्टैंशिएट किया जा सकता है:

def my_macro(name, visibility=None):
  native.cc_library(
    name = name,
    srcs = ["main.cc"],
    visibility = visibility,
  )

अगर आपको पैकेज का नाम जानना है (उदाहरण के लिए, कौनसी BUILD फ़ाइल मैक्रो को कॉल कर रही है), तो Native.package_name() फ़ंक्शन का इस्तेमाल करें. ध्यान दें कि native का इस्तेमाल सिर्फ़ .bzl फ़ाइलों के लिए किया जा सकता है, न कि WORKSPACE या BUILD फ़ाइलों में.

मैक्रो में लेबल रिज़ॉल्यूशन

मैक्रो का मूल्यांकन लोडिंग चरण में किया जाता है, इसलिए किसी मैक्रो में होने वाली "//foo:bar" जैसी लेबल स्ट्रिंग को BUILD फ़ाइल के बारे में समझा जाता है, क्योंकि इसमें .bzl फ़ाइल के मुकाबले मैक्रो का इस्तेमाल किया जाता है. आम तौर पर, यह व्यवहार ऐसे मैक्रो के लिए नहीं होता जो दूसरे डेटा स्टोर करने की जगहों में इस्तेमाल किए जाते हैं. उदाहरण के लिए, वे प्रकाशित किए गए Starlark नियमों का हिस्सा हैं.

Starlark के नियमों जैसा ही काम करने के लिए, लेबल स्ट्रिंग को Label कंस्ट्रक्टर के साथ रैप करें:

# @my_ruleset//rules:defs.bzl
def my_cc_wrapper(name, deps = [], **kwargs):
  native.cc_library(
    name = name,
    deps = deps + select({
      # Due to the use of Label, this label is resolved within @my_ruleset,
      # regardless of its site of use.
      Label("//config:needs_foo"): [
        # Due to the use of Label, this label will resolve to the correct target
        # even if the canonical name of @dep_of_my_ruleset should be different
        # in the main workspace, such as due to repo mappings.
        Label("@dep_of_my_ruleset//tools:foo"),
      ],
      "//conditions:default": [],
    }),
    **kwargs,
  )

डीबग करना

  • bazel query --output=build //my/path:all आपको दिखाएगा कि मूल्यांकन के बाद BUILD फ़ाइल कैसी दिखाई देती है. सभी मैक्रो, ग्लोब, लूप को विस्तृत कर दिया गया है. जानी-पहचानी सीमा: फ़िलहाल, select एक्सप्रेशन, आउटपुट में नहीं दिख रहे हैं.

  • आपके पास generator_function (जो नियम जनरेट करने वाले फ़ंक्शन) या generator_name (मैक्रो का नाम एट्रिब्यूट) के आधार पर आउटपुट को फ़िल्टर कर सकते हैं: bash $ bazel query --output=build 'attr(generator_function, my_macro, //my/path:all)'

  • यह पता लगाने के लिए कि foo फ़ाइल किसी नियम के तहत BUILD फ़ाइल में कहां जनरेट होती है, आप नीचे दिया गया तरीका आज़मा सकते हैं. यह लाइन BUILD फ़ाइल के सबसे ऊपरी हिस्से के पास डालें: cc_library(name = "foo"). Bazel चलाएं. foo नाम बनाने (नाम में विरोध की वजह से) होने पर आपको एक अपवाद मिलेगा. इसमें आपको पूरा ट्रेस ट्रेस दिखेगा.

  • डीबग करने के लिए, प्रिंट का भी इस्तेमाल किया जा सकता है. यह मैसेज, लोडिंग फ़ेज़ के दौरान DEBUG लॉग लाइन के तौर पर दिखाता है. बहुत कम मामलों को छोड़कर, print कॉल हटाएं या उन्हें debugging पैरामीटर में शर्तों के मुताबिक बनाएं. यह पैरामीटर डिपो में कोड सबमिट करने से पहले False तक आता है.

गड़बड़ियां

अगर आपको कोई गड़बड़ी करनी है, तो fall फ़ंक्शन का इस्तेमाल करें. उपयोगकर्ता को साफ़ तौर पर बताएं कि क्या गड़बड़ी हुई है और उसकी BUILD फ़ाइल को कैसे ठीक करें. किसी गड़बड़ी का पता नहीं लगाया जा सकता.

def my_macro(name, deps, visibility=None):
  if len(deps) < 2:
    fail("Expected at least two values in deps")
  # ...

सम्मेलन

  • नियमों को लागू करने वाले सभी सार्वजनिक फ़ंक्शन (अंडरस्कोर से शुरू नहीं होने वाले फ़ंक्शन) में name आर्ग्युमेंट होना ज़रूरी है. यह आर्ग्युमेंट वैकल्पिक नहीं होना चाहिए (कोई डिफ़ॉल्ट वैल्यू न दें).

  • सार्वजनिक फ़ंक्शन को Python की कन्वेंशन का पालन करते हुए docstring का इस्तेमाल करना चाहिए.

  • BUILD फ़ाइलों में, मैक्रो का name आर्ग्युमेंट एक कीवर्ड आर्ग्युमेंट होना चाहिए (पोज़िशनल आर्ग्युमेंट नहीं).

  • मैक्रो से जनरेट किए गए नियमों के name एट्रिब्यूट में, नाम का इस्तेमाल प्रीफ़िक्स के तौर पर किया जाना चाहिए. उदाहरण के लिए, macro(name = "foo") एक cc_library foo और एक संग नियम foo_gen जनरेट कर सकता है.

  • ज़्यादातर मामलों में, वैकल्पिक पैरामीटर की डिफ़ॉल्ट वैल्यू None होनी चाहिए. None को सीधे नेटिव नियमों पर भेजा जा सकता है. ये ऐसे ही होंगे जैसे आपने किसी आर्ग्युमेंट में पास नहीं किए हैं. इसलिए, इसे बदलने के लिए आपको 0, False या [] को बदलने की ज़रूरत नहीं है. इसके बजाय, मैक्रो को उसके बनाए गए नियमों का पालन करना चाहिए, क्योंकि उनके डिफ़ॉल्ट जटिल हो सकते हैं या समय के साथ बदल सकते हैं. इसके अलावा, किसी पैरामीटर को खास तौर पर उसकी डिफ़ॉल्ट वैल्यू पर सेट करने पर, वह क्वेरी लैंग्वेज या बिल्ड-सिस्टम के अंदरूनी डेटा को ऐक्सेस करने पर सेट नहीं किए गए पैरामीटर से अलग दिखता है (या None पर सेट होता है).

  • मैक्रो में एक वैकल्पिक visibility आर्ग्युमेंट होना चाहिए.