मैक्रो

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

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

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

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

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

उदाहरण के लिए, 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 फ़ाइलों में किया जा सकता है, WORKSPACE या BUILD फ़ाइलों में नहीं.

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

मैक्रो का आकलन लोड करने के चरण में किया जाता है. इसलिए, मैक्रो में शामिल "//foo:bar" जैसी लेबल स्ट्रिंग को BUILD फ़ाइल के हिसाब से समझा जाता है, न कि उस .bzl फ़ाइल के मुकाबले जिसमें मैक्रो का इस्तेमाल किया जाता है. आम तौर पर, मैक्रो का ऐसा व्यवहार पसंद न किया जाना चाहिए. ऐसा उन मैक्रो के लिए किया जाता है जिनका इस्तेमाल अन्य डेटा स्टोर करने की जगहों में किया जाना है. उदाहरण के लिए, वे 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)'

  • यह जानने के लिए कि BUILD फ़ाइल में foo नियम कहां जनरेट हुआ है, यहां दिया गया तरीका आज़माएं. BUILD फ़ाइल के ऊपरी हिस्से के पास यह लाइन डालें: cc_library(name = "foo"). रन बेज़ल. नाम में गड़बड़ी होने की वजह से, 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 तर्क होना चाहिए.