मैक्रो

7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

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

इस्तेमाल

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

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

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

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

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 फ़ाइल को Workspace में मौजूद किसी भी पैकेज से लोड किया जा सकता है.

आखिर में, 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,
  )

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

मैक्रो को बड़ा करना

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

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

अगर आपको पैकेज का नाम जानना है, तो native.package_name() फ़ंक्शन का इस्तेमाल करें. उदाहरण के लिए, यह जानने के लिए कि मैक्रो को कौनसी BUILD फ़ाइल कॉल कर रही है. ध्यान दें कि 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 नियम बनाने पर आपको अपवाद मिलेगा, जिससे आपको फ़ुल स्टैक ट्रेस दिखेगा.

  • डीबग करने के लिए, print का भी इस्तेमाल किया जा सकता है. यह मैसेज, लोड करने के दौरान 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 और genrule foo_gen जनरेट कर सकता है.

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

  • मैक्रो में visibility आर्ग्युमेंट होना चाहिए. हालांकि, यह ज़रूरी नहीं है.