मैक्रो

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

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

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

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

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

उदाहरण के लिए, BUILD फ़ाइल में जनरेट किए गए नियम से //: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 में, मूल जनरेट की परिभाषा को एनकैप्सुलेट और पैरामीटर करने के लिए, मैक्रो की परिभाषा लिखें:

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 फ़ाइलों में किया जा सकता है, 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 repo, 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)'

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

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

गड़बड़ियां

अगर आपको कोई गड़बड़ी दिखानी है, तो fail फ़ंक्शन का इस्तेमाल करें. उपयोगकर्ता को साफ़ तौर पर बताएं कि क्या गड़बड़ी हुई है और उनकी 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 तर्क होना चाहिए.