लेगसी मैक्रो

लेगसी मैक्रो, अनस्ट्रक्चर्ड फ़ंक्शन होते हैं. इन्हें 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 फ़ाइल को, वर्कस्पेस में मौजूद किसी भी पैकेज से लोड किया जा सकता है.

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

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

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

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

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

यह जानने के लिए कि कोई मैक्रो क्या करता है, 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,
  )

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

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

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

--incompatible_resolve_select_keys_eagerly फ़्लैग चालू होने पर, लेबल स्ट्रिंग वाली सभी कुंजियों को, select कॉल वाली फ़ाइल के पैकेज के हिसाब से, Label ऑब्जेक्ट में अपने-आप रिज़ॉल्व कर दिया जाएगा. अगर यह विकल्प नहीं चुना जाता है, तो लेबल स्ट्रिंग को native.package_relative_label()में रैप करें.

डीबग करना

  • 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 नियम कहां जनरेट किया गया है, यह तरीका आज़माया जा सकता है. फ़ाइल में सबसे ऊपर के पास यह लाइन डालें: BUILDcc_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 के कन्वेंशन के मुताबिक डॉकस्ट्रिंग का इस्तेमाल किया जाना चाहिए.

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

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

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

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