इस पेज पर, मैक्रो इस्तेमाल करने के बुनियादी तरीके के बारे में बताया गया है. साथ ही, इसमें इस्तेमाल के सामान्य उदाहरण, डीबग करने का तरीका, और कॉन्वेंशन शामिल हैं.
मैक्रो, BUILD
फ़ाइल से कॉल किया जाने वाला एक फ़ंक्शन है. यह नियमों को लागू कर सकता है.
मैक्रो का इस्तेमाल मुख्य रूप से, मौजूदा नियमों और अन्य मैक्रो को एन्कैप्सुलेट करने और कोड का फिर से इस्तेमाल करने के लिए किया जाता है.
मैक्रो दो तरह के होते हैं: सिंबल मैक्रो, जिनके बारे में इस पेज पर बताया गया है और लेगसी मैक्रो. हमारा सुझाव है कि जहां भी हो सके, कोड को साफ़ तौर पर समझने के लिए, सिंबल मैक्रो का इस्तेमाल करें.
सिंबल मैक्रो में टाइप किए गए आर्ग्युमेंट (स्ट्रिंग से लेबल में कन्वर्ज़न, मैक्रो को कहां से कॉल किया गया) और बनाए गए टारगेट की दिखने की सुविधा को सीमित करने और तय करने की सुविधा मिलती है. इन्हें धीरे-धीरे जांच करने के लिए डिज़ाइन किया गया है. यह सुविधा, Bazel के आने वाले वर्शन में जोड़ी जाएगी. सिंबल मैक्रो, Bazel 8 में डिफ़ॉल्ट रूप से उपलब्ध होते हैं. इस दस्तावेज़ में macros
का उल्लेख, सिंबल मैक्रो के बारे में बताने के लिए किया गया है.
सिंबल मैक्रो का एक उदाहरण, उदाहरणों के रिपॉज़िटरी में देखा जा सकता है.
इस्तेमाल
मैक्रो को .bzl
फ़ाइलों में तय किया जाता है. इसके लिए, macro()
फ़ंक्शन को दो ज़रूरी पैरामीटर के साथ कॉल किया जाता है: attrs
और implementation
.
विशेषताएं
attrs
, एट्रिब्यूट के नाम से एट्रिब्यूट टाइप की डिक्शनरी स्वीकार करता है. यह मैक्रो के लिए आर्ग्युमेंट दिखाता है. दो सामान्य एट्रिब्यूट – name
और visibility
–
सभी मैक्रो में अपने-आप जुड़ जाते हैं. साथ ही, इन्हें attrs
में पास की गई डिक्शनरी में शामिल नहीं किया जाता.
# macro/macro.bzl
my_macro = macro(
attrs = {
"deps": attr.label_list(mandatory = True, doc = "The dependencies passed to the inner cc_binary and cc_test targets"),
"create_test": attr.bool(default = False, configurable = False, doc = "If true, creates a test target"),
},
implementation = _my_macro_impl,
)
एट्रिब्यूट टाइप के एलान में, पैरामीटर, mandatory
, default
, और doc
का इस्तेमाल किया जा सकता है. ज़्यादातर एट्रिब्यूट टाइप में configurable
पैरामीटर भी काम करता है. इससे यह तय होता है कि एट्रिब्यूट में select
इस्तेमाल किए जा सकते हैं या नहीं. अगर कोई एट्रिब्यूट configurable
है, तो वह select
के अलावा किसी दूसरी वैल्यू को, कॉन्फ़िगर नहीं की जा सकने वाली select
के तौर पर पार्स करेगा – "foo"
, select({"//conditions:default": "foo"})
बन जाएगा. ज़्यादा जानकारी के लिए, चुनें पर जाएं.
एट्रिब्यूट इनहेरिटेंस
मैक्रो का मकसद, अक्सर किसी नियम (या किसी दूसरे मैक्रो) को रैप करना होता है. साथ ही, मैक्रो का लेखक अक्सर **kwargs
का इस्तेमाल करके, मैक्रो के मुख्य टारगेट (या मुख्य इनर मैक्रो) पर, रैप किए गए सिंबल के ज़्यादातर एट्रिब्यूट को बिना बदलाव के फ़ॉरवर्ड करना चाहता है.
इस पैटर्न के साथ काम करने के लिए, कोई मैक्रो किसी नियम या किसी दूसरे मैक्रो से एट्रिब्यूट इनहेरिट कर सकता है. इसके लिए, macro()
के inherit_attrs
आर्ग्युमेंट में नियम या मैक्रो सिंबल डाला जाता है. (सभी Starlark बिल्ड नियमों के लिए तय किए गए सामान्य एट्रिब्यूट को इनहेरिट करने के लिए, किसी नियम या मैक्रो सिंबल के बजाय, खास स्ट्रिंग "common"
का भी इस्तेमाल किया जा सकता है.)
सिर्फ़ सार्वजनिक एट्रिब्यूट इनहेरिट किए जाते हैं. साथ ही, मैक्रो की अपनी attrs
डिक्शनरी में मौजूद एट्रिब्यूट, एक ही नाम वाले इनहेरिट किए गए एट्रिब्यूट को बदल देते हैं. attrs
डिक्शनरी में वैल्यू के तौर पर None
का इस्तेमाल करके, इनहेरिट किए गए एट्रिब्यूट को हटाया भी जा सकता है:
# macro/macro.bzl
my_macro = macro(
inherit_attrs = native.cc_library,
attrs = {
# override native.cc_library's `local_defines` attribute
local_defines = attr.string_list(default = ["FOO"]),
# do not inherit native.cc_library's `defines` attribute
defines = None,
},
...
)
इनहेरिट किए गए ऐसे एट्रिब्यूट की डिफ़ॉल्ट वैल्यू को हमेशा None
पर सेट किया जाता है जो ज़रूरी नहीं हैं. भले ही, एट्रिब्यूट की मूल परिभाषा की डिफ़ॉल्ट वैल्यू कुछ भी हो. अगर आपको इनहेरिट किए गए किसी ऐसे एट्रिब्यूट की जांच करनी है या उसमें बदलाव करना है जो ज़रूरी नहीं है, तो आपको अपने मैक्रो के लागू करने वाले फ़ंक्शन में None
केस को मैनेज करना होगा. उदाहरण के लिए, अगर आपको इनहेरिट किए गए tags
एट्रिब्यूट में कोई टैग जोड़ना है, तो:
# macro/macro.bzl
_my_macro_implementation(name, visibility, tags, **kwargs):
# Append a tag; tags attr is an inherited non-mandatory attribute, and
# therefore is None unless explicitly set by the caller of our macro.
my_tags = (tags or []) + ["another_tag"]
native.cc_library(
...
tags = my_tags,
**kwargs,
)
...
लागू करना
implementation
, ऐसे फ़ंक्शन को स्वीकार करता है जिसमें मैक्रो का लॉजिक शामिल होता है.
लागू करने वाले फ़ंक्शन, अक्सर एक या उससे ज़्यादा नियमों को कॉल करके टारगेट बनाते हैं. आम तौर पर, ये निजी होते हैं (इनके नाम में अंडरस्कोर होता है). आम तौर पर, इनका नाम मैक्रो के नाम जैसा ही होता है. हालांकि, इनके नाम के आगे _
और आखिर में _impl
होता है.
नियम लागू करने वाले फ़ंक्शन, एक आर्ग्युमेंट (ctx
) लेते हैं. इसमें एट्रिब्यूट का रेफ़रंस होता है. वहीं, मैक्रो लागू करने वाले फ़ंक्शन, हर आर्ग्युमेंट के लिए एक पैरामीटर लेते हैं.
# macro/macro.bzl
def _my_macro_impl(name, visibility, deps, create_test):
cc_library(
name = name + "_cc_lib",
deps = deps,
)
if create_test:
cc_test(
name = name + "_test",
srcs = ["my_test.cc"],
deps = deps,
)
अगर किसी मैक्रो को एट्रिब्यूट इनहेरिट किए जाते हैं, तो उसके लागू करने वाले फ़ंक्शन में **kwargs
अवशेष कीवर्ड पैरामीटर होना चाहिए. इसे उस कॉल पर फ़ॉरवर्ड किया जा सकता है जो इनहेरिट किए गए नियम या सबमैक्रो को लागू करता है. (इससे यह पक्का करने में मदद मिलती है कि अगर आपने जिस नियम या मैक्रो से इनहेरिट किया है वह नया एट्रिब्यूट जोड़ता है, तो आपका मैक्रो काम करना बंद नहीं करेगा.)
एलान
मैक्रो का एलान करने के लिए, BUILD
फ़ाइल में उनकी परिभाषा को लोड करके उसे कॉल किया जाता है.
# pkg/BUILD
my_macro(
name = "macro_instance",
deps = ["src.cc"] + select(
{
"//config_setting:special": ["special_source.cc"],
"//conditions:default": [],
},
),
create_tests = True,
)
इससे टारगेट
//pkg:macro_instance_cc_lib
और//pkg:macro_instance_test
बन जाएंगे.
नियम कॉल की तरह ही, अगर मैक्रो कॉल में किसी एट्रिब्यूट की वैल्यू None
पर सेट है, तो उस एट्रिब्यूट को ऐसे माना जाता है जैसे मैक्रो कॉलर ने उसे छोड़ा हो. उदाहरण के लिए, ये दोनों मैक्रो कॉल एक जैसे हैं:
# pkg/BUILD
my_macro(name = "abc", srcs = ["src.cc"], deps = None)
my_macro(name = "abc", srcs = ["src.cc"])
आम तौर पर, यह BUILD
फ़ाइलों में काम का नहीं होता. हालांकि, प्रोग्राम के हिसाब से किसी मैक्रो को किसी दूसरे मैक्रो में रैप करने पर, यह मददगार होता है.
विवरण
बनाए गए टारगेट के लिए नेमिंग कन्वेंशन
सिंबल वाले मैक्रो से बनाए गए किसी भी टारगेट या सब-मैक्रो के नाम, मैक्रो के name
पैरामीटर से मैच होने चाहिए. इसके अलावा, इन नामों के आगे name
और उसके बाद _
(इसका सुझाव दिया जाता है), .
या -
होना चाहिए. उदाहरण के लिए, my_macro(name = "foo")
सिर्फ़ foo
नाम वाली फ़ाइलें या टारगेट बना सकता है. इसके अलावा, foo_
, foo-
या foo.
से शुरू होने वाली फ़ाइलें या टारगेट भी बनाए जा सकते हैं. जैसे, foo_bar
.
मैक्रो के नाम रखने के कन्वेंशन का उल्लंघन करने वाले टारगेट या फ़ाइलों का एलान किया जा सकता है. हालांकि, इन्हें बिल्ट नहीं किया जा सकता और न ही इनका इस्तेमाल डिपेंडेंसी के तौर पर किया जा सकता है.
मैक्रो इंस्टेंस के तौर पर एक ही पैकेज में मौजूद, मैक्रो फ़ाइलों और टारगेट के नाम, मैक्रो टारगेट के संभावित नामों से अलग होने चाहिए. हालांकि, यह ज़रूरी नहीं है कि ऐसा ही हो. हम सिंबल मैक्रो की परफ़ॉर्मेंस को बेहतर बनाने के लिए, लेज़ी इवैल्यूएशन लागू करने की प्रोसेस में हैं. यह सुविधा, नाम देने के स्कीमा का उल्लंघन करने वाले पैकेज में काम नहीं करेगी.
पाबंदियां
लेगसी मैक्रो की तुलना में, सिंबल मैक्रो पर कुछ और पाबंदियां हैं.
सिम्बॉलिक मैक्रो
name
आर्ग्युमेंट औरvisibility
आर्ग्युमेंट लेना चाहिएimplementation
फ़ंक्शन होना चाहिए- वैल्यू नहीं दिखा सकता
- अपने आर्ग्युमेंट में बदलाव नहीं कर सकते
native.existing_rules()
को तब तक कॉल नहीं कर सकते, जब तक वे खासfinalizer
मैक्रो न होंnative.package()
को कॉल नहीं कर सकताglob()
को कॉल नहीं कर सकताnative.environment_group()
को कॉल नहीं कर सकता- ऐसे टारगेट बनाने चाहिए जिनके नाम, नाम रखने के स्कीमा के मुताबिक हों
- उन इनपुट फ़ाइलों का रेफ़रंस नहीं दिया जा सकता जिन्हें एर्ग्युमेंट के तौर पर एलान नहीं किया गया है या पास नहीं किया गया है (ज़्यादा जानकारी के लिए, प्रॉपर्टी के दिखने की सेटिंग और मैक्रो देखें).
विज़िबिलिटी और मैक्रो
दिखाई देने की सुविधा, मैक्रो और उन्हें कॉल करने वाले, दोनों के लागू होने की जानकारी को सुरक्षित रखने में मदद करती है.
डिफ़ॉल्ट रूप से, सिंबल वाले मैक्रो में बनाए गए टारगेट, मैक्रो में दिखते हैं. हालांकि, यह ज़रूरी नहीं है कि वे मैक्रो को कॉल करने वाले को दिखें. मैक्रो, अपने visibility
एट्रिब्यूट की वैल्यू को फ़ॉरवर्ड करके, टारगेट को सार्वजनिक एपीआई के तौर पर "एक्सपोर्ट" कर सकता है. जैसे, some_rule(..., visibility = visibility)
में बताया गया है.
मैक्रो विज़िबिलिटी के मुख्य आइडिया ये हैं:
विज़िबिलिटी की जांच इस आधार पर की जाती है कि किस मैक्रो ने टारगेट को डिक्लेयर्ड किया है, न कि किस पैकेज ने मैक्रो को कॉल किया है.
- दूसरे शब्दों में, एक ही पैकेज में होने से, एक टारगेट अपने-आप दूसरे को नहीं दिखता. इससे, मैक्रो के इंटरनल टारगेट को पैकेज में मौजूद अन्य मैक्रो या टॉप-लेवल टारगेट की डिपेंडेंसी बनने से बचाया जाता है.
नियमों और मैक्रो, दोनों में सभी
visibility
एट्रिब्यूट में, उस जगह की जानकारी अपने-आप शामिल हो जाती है जहां नियम या मैक्रो को कॉल किया गया था.- इसलिए, एक टारगेट, उसी मैक्रो (या मैक्रो में न होने पर
BUILD
फ़ाइल) में बताए गए अन्य टारगेट को बिना किसी शर्त के दिखता है.
- इसलिए, एक टारगेट, उसी मैक्रो (या मैक्रो में न होने पर
इसका मतलब है कि जब कोई मैक्रो, visibility
सेट किए बिना कोई टारगेट तय करता है, तो टारगेट डिफ़ॉल्ट रूप से मैक्रो के अंदरूनी टारगेट के तौर पर सेट हो जाता है. (पैकेज की डिफ़ॉल्ट तौर पर दिखने की सेटिंग, मैक्रो में लागू नहीं होती.) टारगेट को एक्सपोर्ट करने का मतलब है कि टारगेट, मैक्रो के visibility
एट्रिब्यूट में मैक्रो के कॉलर के बताए गए सभी एलिमेंट के साथ-साथ, मैक्रो के कॉलर के पैकेज और मैक्रो के कोड को दिखता है.
इसे इस तरह भी समझा जा सकता है कि मैक्रो के दिखने से यह तय होता है कि मैक्रो के एक्सपोर्ट किए गए टारगेट को मैक्रो के अलावा कौन देख सकता है.
# tool/BUILD
...
some_rule(
name = "some_tool",
visibility = ["//macro:__pkg__"],
)
# macro/macro.bzl
def _impl(name, visibility):
cc_library(
name = name + "_helper",
...
# No visibility passed in. Same as passing `visibility = None` or
# `visibility = ["//visibility:private"]`. Visible to the //macro
# package only.
)
cc_binary(
name = name + "_exported",
deps = [
# Allowed because we're also in //macro. (Targets in any other
# instance of this macro, or any other macro in //macro, can see it
# too.)
name + "_helper",
# Allowed by some_tool's visibility, regardless of what BUILD file
# we're called from.
"//tool:some_tool",
],
...
visibility = visibility,
)
my_macro = macro(implementation = _impl, ...)
# pkg/BUILD
load("//macro:macro.bzl", "my_macro")
...
my_macro(
name = "foo",
...
)
some_rule(
...
deps = [
# Allowed, its visibility is ["//pkg:__pkg__", "//macro:__pkg__"].
":foo_exported",
# Disallowed, its visibility is ["//macro:__pkg__"] and
# we are not in //macro.
":foo_helper",
]
)
अगर my_macro
को visibility = ["//other_pkg:__pkg__"]
के साथ कॉल किया गया था या //pkg
पैकेज ने अपनी default_visibility
को उस वैल्यू पर सेट किया था, तो //pkg:foo_exported
का इस्तेमाल //other_pkg/BUILD
में या //other_pkg:defs.bzl
में तय किए गए मैक्रो में भी किया जा सकता है. हालांकि, //pkg:foo_helper
सुरक्षित रहेगा.
मैक्रो यह बता सकता है कि कोई टारगेट, फ़्रेंड पैकेज को दिख रहा है. इसके लिए, मैक्रो में visibility = ["//some_friend:__pkg__"]
(इंटरनल टारगेट के लिए) या visibility = visibility + ["//some_friend:__pkg__"]
(एक्सपोर्ट किए गए टारगेट के लिए) को पास किया जाता है.
ध्यान दें कि मैक्रो के लिए, सार्वजनिक तौर पर दिखने वाले (visibility = ["//visibility:public"]
) टारगेट का एलान करना एक गलत तरीका है. इसकी वजह यह है कि इससे टारगेट, हर पैकेज के लिए बिना किसी शर्त के दिखता है. भले ही, कॉलर ने टारगेट के दिखने की ज़्यादा पाबंदी तय की हो.
विज़िबिलिटी की सभी जांच, फ़िलहाल चल रहे सबसे अंदरूनी सिंबल मैक्रो के हिसाब से की जाती है. हालांकि, लेबल को दिखने की अनुमति देने का एक तरीका है: अगर कोई मैक्रो, किसी लेबल को एट्रिब्यूट की वैल्यू के तौर पर किसी इनर मैक्रो को पास करता है, तो इनर मैक्रो में लेबल के किसी भी इस्तेमाल की जांच, आउटर मैक्रो के हिसाब से की जाती है. ज़्यादा जानकारी के लिए, दिखने की सेटिंग वाला पेज देखें.
याद रखें कि लेगसी मैक्रो, विज़िबिलिटी सिस्टम के लिए पूरी तरह से पारदर्शी होते हैं. साथ ही, वे इस तरह काम करते हैं जैसे उनकी जगह वही BUILD फ़ाइल या सिंबल मैक्रो हो जिससे उन्हें कॉल किया गया था.
चुनता है
अगर कोई एट्रिब्यूट configurable
(डिफ़ॉल्ट) है और उसकी वैल्यू None
नहीं है, तो मैक्रो लागू करने वाले फ़ंक्शन को एट्रिब्यूट की वैल्यू, सामान्य select
में रैप की गई के तौर पर दिखेगी. इससे मैक्रो के लेखक को उन गड़बड़ियों का पता लगाना आसान हो जाता है जहां उन्हें उम्मीद नहीं थी कि एट्रिब्यूट की वैल्यू select
हो सकती है.
उदाहरण के लिए, नीचे दिया गया मैक्रो देखें:
my_macro = macro(
attrs = {"deps": attr.label_list()}, # configurable unless specified otherwise
implementation = _my_macro_impl,
)
अगर my_macro
को deps = ["//a"]
के साथ शुरू किया जाता है, तो _my_macro_impl
को deps
पैरामीटर को select({"//conditions:default":
["//a"]})
पर सेट करके शुरू किया जाएगा. अगर इस वजह से लागू करने का फ़ंक्शन काम नहीं करता है (उदाहरण के लिए, कोड ने deps[0]
की तरह वैल्यू को इंडेक्स करने की कोशिश की, जो select
के लिए अनुमति नहीं है), तो मैक्रो बनाने वाला व्यक्ति इनमें से कोई एक विकल्प चुन सकता है: वह अपने मैक्रो को फिर से लिख सकता है, ताकि सिर्फ़ select
के साथ काम करने वाले ऑपरेशन का इस्तेमाल किया जा सके या वह एट्रिब्यूट को कॉन्फ़िगर न किए जा सकने वाले (attr.label_list(configurable = False)
) के तौर पर मार्क कर सकता है.select
नियम के टारगेट, इस बदलाव को उलट देते हैं और सामान्य select
को बिना शर्त वाली वैल्यू के तौर पर सेव करते हैं. ऊपर दिए गए उदाहरण में, अगर _my_macro_impl
किसी नियम के टारगेट my_rule(..., deps = deps)
को दिखाता है, तो उस नियम के टारगेट का deps
, ["//a"]
के तौर पर सेव किया जाएगा. इससे यह पक्का होता है कि select
-रैपिंग की वजह से, मैक्रो से इंस्टैंशिएट किए गए सभी टारगेट में, सामान्य select
वैल्यू सेव न हों.
अगर कॉन्फ़िगर किए जा सकने वाले एट्रिब्यूट की वैल्यू None
है, तो उसे select
में रैप नहीं किया जाता. इससे यह पक्का होता है कि my_attr == None
जैसे टेस्ट अब भी काम करते हैं. साथ ही, जब एट्रिब्यूट को कैलकुलेट किए गए डिफ़ॉल्ट वैल्यू वाले नियम पर फ़ॉरवर्ड किया जाता है, तो नियम ठीक से काम करता है. इसका मतलब है कि एट्रिब्यूट को बिलकुल भी पास नहीं किया गया है. किसी एट्रिब्यूट के लिए, None
वैल्यू का इस्तेमाल करना हमेशा संभव नहीं होता. हालांकि, attr.label()
टाइप के लिए और इनहेरिट किए गए ऐसे एट्रिब्यूट के लिए ऐसा किया जा सकता है जो ज़रूरी नहीं है.
फ़ाइनल करने वाले
नियम फ़ाइनलाइज़र एक खास सिंबल मैक्रो है. BUILD फ़ाइल में, इसके लेक्सिकल क्रम से कोई फ़र्क़ नहीं पड़ता. पैकेज लोड करने के आखिरी चरण में, सभी नॉन-फ़ाइनलाइज़र टारगेट तय होने के बाद, इसका आकलन किया जाता है. सामान्य सिंबल मैक्रो के मुकाबले, फ़ाइनलाइज़र native.existing_rules()
को कॉल कर सकता है. यहां यह लेगसी मैक्रो से थोड़ा अलग तरीके से काम करता है: यह सिर्फ़ उन टारगेट का सेट दिखाता है जो फ़ाइनलाइज़र नियम के दायरे में नहीं आते. फ़ाइनलाइज़र, उस सेट की स्थिति पर दावा कर सकता है या नए टारगेट तय कर सकता है.
फ़ाइनलाइज़र का एलान करने के लिए, finalizer = True
के साथ macro()
को कॉल करें:
def _my_finalizer_impl(name, visibility, tags_filter):
for r in native.existing_rules().values():
for tag in r.get("tags", []):
if tag in tags_filter:
my_test(
name = name + "_" + r["name"] + "_finalizer_test",
deps = [r["name"]],
data = r["srcs"],
...
)
continue
my_finalizer = macro(
attrs = {"tags_filter": attr.string_list(configurable = False)},
implementation = _impl,
finalizer = True,
)
आलस
अहम जानकारी: हम धीरे-धीरे मैक्रो एक्सपैंशन और आकलन की सुविधा लागू कर रहे हैं. यह सुविधा फ़िलहाल उपलब्ध नहीं है.
फ़िलहाल, BUILD फ़ाइल लोड होने के साथ ही सभी मैक्रो का आकलन किया जाता है. इससे उन पैकेज के टारगेट की परफ़ॉर्मेंस पर बुरा असर पड़ सकता है जिनमें काम के नहीं, बल्कि महंगे मैक्रो भी शामिल होते हैं. आने वाले समय में, नॉन-फ़ाइनलाइज़र सिंबल मैक्रो का आकलन सिर्फ़ तब किया जाएगा, जब वे बिल्ड के लिए ज़रूरी हों. प्रीफ़िक्स नेमिंग स्कीमा की मदद से, Bazel यह तय करता है कि अनुरोध किए गए टारगेट के हिसाब से, किस मैक्रो को बड़ा करना है.
माइग्रेशन से जुड़ी समस्या हल करना
माइग्रेशन से जुड़ी कुछ आम समस्याएं और उन्हें ठीक करने का तरीका यहां बताया गया है.
- लेगसी मैक्रो कॉल
glob()
glob()
कॉल को अपनी BUILD फ़ाइल (या BUILD फ़ाइल से कॉल किए गए किसी लेगसी मैक्रो) में ले जाएं और label-list एट्रिब्यूट का इस्तेमाल करके, glob()
वैल्यू को सिंबल मैक्रो में पास करें:
# BUILD file
my_macro(
...,
deps = glob(...),
)
- लेगसी मैक्रो में एक पैरामीटर है, जो starlark
attr
टाइप का मान्य नहीं है.
नेस्ट किए गए सिंबल वाले मैक्रो में ज़्यादा से ज़्यादा लॉजिक शामिल करें. हालांकि, टॉप लेवल मैक्रो को लेगसी मैक्रो बनाए रखें.
- लेगसी मैक्रो, ऐसे नियम को कॉल करता है जो नाम देने के स्कीमा का उल्लंघन करने वाला टारगेट बनाता है
कोई बात नहीं, बस "आपत्तिजनक" टारगेट पर निर्भर न रहें. नाम की जांच को अनदेखा कर दिया जाएगा.