इस पेज पर, ऐस्पेक्ट इस्तेमाल करने के बुनियादी तरीकों और फ़ायदों के बारे में बताया गया है. साथ ही, आसान और बेहतर उदाहरण भी दिए गए हैं.
ऐस्पेक्ट की मदद से, अतिरिक्त जानकारी और कार्रवाइयों के साथ बिल्ड डिपेंडेंसी ग्राफ़ को बेहतर बनाया जा सकता है. कुछ सामान्य स्थितियां, जब पहलू उपयोगी हो सकते हैं:
- Bazel को इंटिग्रेट करने वाले आईडीई, प्रोजेक्ट के बारे में जानकारी इकट्ठा करने के लिए, ऐस्पेक्ट का इस्तेमाल कर सकते हैं.
- कोड जनरेट करने वाले टूल, टारगेट-एग्नोस्टिक तरीके से अपने इनपुट पर एक्ज़ीक्यूट करने के लिए, पहलुओं का फ़ायदा ले सकते हैं. उदाहरण के लिए,
BUILD
फ़ाइलें protobuf लाइब्रेरी की परिभाषाओं की हैरारकी तय कर सकती हैं. साथ ही, भाषा के हिसाब से बने नियम, किसी खास भाषा के लिए protobuf सहायता कोड जनरेट करने वाली कार्रवाइयों को अटैच करने के लिए, आसपेक्ट का इस्तेमाल कर सकते हैं.
आसपेक्ट से जुड़ी बुनियादी बातें
BUILD
फ़ाइलें, किसी प्रोजेक्ट के सोर्स कोड के बारे में जानकारी देती हैं: प्रोजेक्ट में कौनसी सोर्स फ़ाइलें शामिल हैं, उन फ़ाइलों से कौनसे आर्टफ़ैक्ट (टारगेट) बनाए जाने चाहिए, उन फ़ाइलों के बीच कौनसी डिपेंडेंसी हैं वगैरह. Bazel, बिल्ड करने के लिए इस जानकारी का इस्तेमाल करता है. इसका मतलब है कि यह आर्टफ़ैक्ट बनाने के लिए ज़रूरी कार्रवाइयों (जैसे, कंपाइलर या लिंकर चलाना) का सेट तय करता है और उन कार्रवाइयों को पूरा करता है. Bazel, टारगेट के बीच डिपेंडेंसी ग्राफ़ बनाकर और उन कार्रवाइयों को इकट्ठा करने के लिए इस ग्राफ़ पर जाकर, यह काम करता है.
यहां दी गई BUILD
फ़ाइल देखें:
java_library(name = 'W', ...)
java_library(name = 'Y', deps = [':W'], ...)
java_library(name = 'Z', deps = [':W'], ...)
java_library(name = 'Q', ...)
java_library(name = 'T', deps = [':Q'], ...)
java_library(name = 'X', deps = [':Y',':Z'], runtime_deps = [':T'], ...)
यह BUILD
फ़ाइल, इस फ़िगर में दिखाए गए डिपेंडेंसी ग्राफ़ के बारे में बताती है:
पहली इमेज. BUILD
फ़ाइल का डिपेंडेंसी ग्राफ़.
Baज़ल, इस डिपेंडेंसी ग्राफ़ का विश्लेषण करने के लिए, ऊपर दिए गए उदाहरण में मौजूद हर टारगेट के लिए, इससे जुड़े नियम (इस मामले में "java_library") को लागू करने के फ़ंक्शन को कॉल करते हैं. नियम लागू करने वाले फ़ंक्शन, ऐसी कार्रवाइयां जनरेट करते हैं जो .jar
फ़ाइलों जैसे आर्टफ़ैक्ट बनाते हैं. साथ ही, प्रोवाइडर में उन टारगेट की रिवर्स डिपेंडेंसी को जानकारी देते हैं, जैसे कि उन आर्टफ़ैक्ट की जगह और नाम.
ऐसेट, नियमों से मिलती-जुलती होती हैं. इनमें लागू करने का एक फ़ंक्शन होता है, जो कार्रवाइयां जनरेट करता है और सेवा देने वाली कंपनियों की जानकारी दिखाता है. हालांकि, इनकी ताकत इस बात पर निर्भर करती है कि उनके लिए डिपेंडेंसी ग्राफ़ कैसे बनाया गया है. किसी पहलू को लागू करने का तरीका और उन सभी एट्रिब्यूट की सूची होती है जिन्हें वह प्रॉडक्ट के साथ दिखाता है. एक ऐसे एस्पेक्ट A पर विचार करें जो "deps" नाम के एट्रिब्यूट के साथ प्रॉपर्टी में लागू होता है. इस ऐस्पेक्ट को किसी टारगेट X पर लागू किया जा सकता है, जिससे ऐस्पेक्ट ऐप्लिकेशन नोड A(X) बनता है. इसे लागू करने के दौरान, A
इस तरह, किसी टारगेट X पर एस्पेक्ट A लागू करने से, टारगेट के मूल डिपेंडेंसी ग्राफ़ का "शैडो ग्राफ़" बन जाता है. यह ग्राफ़, नीचे दिए गए चित्र में दिखाया गया है:
दूसरी इमेज. पहलुओं के साथ ग्राफ़ बनाएं.
सिर्फ़ प्रॉपेगेशन सेट में मौजूद एट्रिब्यूट के किनारों को ही शेडो किया जाता है. इसलिए, इस उदाहरण में runtime_deps
एज को शेडो नहीं किया गया है. इसके बाद, शैडो ग्राफ़ के सभी नोड पर, ऐस्पेक्ट लागू करने वाला फ़ंक्शन लागू किया जाता है. यह उसी तरह होता है जिस तरह ओरिजनल ग्राफ़ के नोड पर नियम लागू किए जाते हैं.
आसान उदाहरण
इस उदाहरण में, किसी नियम और उसकी सभी डिपेंडेंसी के लिए सोर्स फ़ाइलों को बार-बार प्रिंट करने का तरीका बताया गया है. इन डिपेंडेंसी में deps
एट्रिब्यूट होना चाहिए. इसमें, आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) लागू करने के साथ-साथ, बेज़ेल कमांड लाइन से आसपेक्ट रेशियो को शुरू करने का तरीका बताया गया है.
def _print_aspect_impl(target, ctx):
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the files that make up the sources and
# print their paths.
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
print(f.path)
return []
print_aspect = aspect(
implementation = _print_aspect_impl,
attr_aspects = ['deps'],
)
आइए, हम उदाहरण को अलग-अलग हिस्सों में बांटते हैं और हर एक की अलग-अलग जांच करते हैं.
आसपेक्ट की परिभाषा
print_aspect = aspect(
implementation = _print_aspect_impl,
attr_aspects = ['deps'],
)
आसपेक्ट की परिभाषाएं, नियम की परिभाषाओं से मेल खाती हैं और इन्हें aspect
फ़ंक्शन का इस्तेमाल करके तय किया जाता है.
नियम की तरह ही, किसी पहलू को लागू करने का फ़ंक्शन भी होता है. इस मामले में, यह फ़ंक्शन
_print_aspect_impl
है.
attr_aspects
, नियम के उन एट्रिब्यूट की सूची है जिनके साथ आसपेक्ट लागू होता है.
इस मामले में, यह आसपेक्ट उन नियमों के deps
एट्रिब्यूट के साथ प्रॉगेट होगा जिन पर इसे लागू किया गया है.
attr_aspects
के लिए एक और सामान्य आर्ग्युमेंट ['*']
है, जो किसी नियम के सभी एट्रिब्यूट में एस्पेक्ट को प्रॉपैगेट करेगा.
आसपेक्ट रेशियो लागू करना
def _print_aspect_impl(target, ctx):
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the files that make up the sources and
# print their paths.
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
print(f.path)
return []
'आसपेक्ट लागू करने' के फ़ंक्शन, नियम को लागू करने वाले फ़ंक्शन की तरह ही होते हैं. ये प्रोवाइडर दिखाते हैं, कार्रवाइयां जनरेट कर सकते हैं, और दो आर्ग्युमेंट लेते हैं:
target
: वह टारगेट जिस पर आसपेक्ट लागू किया जा रहा है.ctx
:ctx
ऑब्जेक्ट, जिसका इस्तेमाल एट्रिब्यूट ऐक्सेस करने और आउटपुट और कार्रवाइयां जनरेट करने के लिए किया जा सकता है.
लागू करने वाला फ़ंक्शन, ctx.rule.attr
के ज़रिए टारगेट नियम के एट्रिब्यूट को ऐक्सेस कर सकता है. इस सुविधा की मदद से, सेवा देने वाली उन कंपनियों की जांच की जा सकती है जो टारगेट के तहत दी जाती हैं. ऐसा करने के लिए, target
आर्ग्युमेंट का इस्तेमाल किया जा सकता है.
सेवा देने वालों की सूची दिखाने के लिए, आसपेक्ट की ज़रूरत होती है. इस उदाहरण में, आसपेक्ट रेशियो कुछ नहीं दिखाता है. इसलिए, यह खाली सूची दिखाता है.
कमांड लाइन का इस्तेमाल करके, ऐस्पेक्ट को चालू करना
किसी आसपेक्ट को लागू करने का सबसे आसान तरीका, कमांड लाइन से --aspects
आर्ग्युमेंट का इस्तेमाल करना है. मान लें कि ऊपर दिए गए पहलू को print.bzl
नाम की फ़ाइल में बताया गया था:
bazel build //MyExample:example --aspects print.bzl%print_aspect
print_aspect
को टारगेट example
और उन सभी टारगेट नियमों पर लागू करेगा जो deps
एट्रिब्यूट के ज़रिए बार-बार ऐक्सेस किए जा सकते हैं.
--aspects
फ़्लैग में एक आर्ग्युमेंट होता है, जो <extension file label>%<aspect top-level name>
फ़ॉर्मैट में आसपेक्ट की जानकारी देता है.
बेहतर उदाहरण
यहां दिए गए उदाहरण में, टारगेट नियम के ऐसे हिस्से का इस्तेमाल दिखाया गया है जो टारगेट में मौजूद फ़ाइलों की गिनती करता है. साथ ही, उन्हें एक्सटेंशन के हिसाब से फ़िल्टर करता है. इसमें, वैल्यू दिखाने के लिए किसी प्रोवाइडर का इस्तेमाल करने का तरीका, किसी पैरामीटर को किसी ऐस्पेक्ट के लागू होने के दौरान, आर्ग्युमेंट के तौर पर इस्तेमाल करने का तरीका, और किसी नियम से ऐस्पेक्ट को लागू करने का तरीका बताया गया है.
file_count.bzl
फ़ाइल:
FileCountInfo = provider(
fields = {
'count' : 'number of files'
}
)
def _file_count_aspect_impl(target, ctx):
count = 0
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the sources counting files
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
count = count + 1
# Get the counts from our dependencies.
for dep in ctx.rule.attr.deps:
count = count + dep[FileCountInfo].count
return [FileCountInfo(count = count)]
file_count_aspect = aspect(
implementation = _file_count_aspect_impl,
attr_aspects = ['deps'],
attrs = {
'extension' : attr.string(values = ['*', 'h', 'cc']),
}
)
def _file_count_rule_impl(ctx):
for dep in ctx.attr.deps:
print(dep[FileCountInfo].count)
file_count_rule = rule(
implementation = _file_count_rule_impl,
attrs = {
'deps' : attr.label_list(aspects = [file_count_aspect]),
'extension' : attr.string(default = '*'),
},
)
BUILD.bazel
फ़ाइल:
load('//:file_count.bzl', 'file_count_rule')
cc_library(
name = 'lib',
srcs = [
'lib.h',
'lib.cc',
],
)
cc_binary(
name = 'app',
srcs = [
'app.h',
'app.cc',
'main.cc',
],
deps = ['lib'],
)
file_count_rule(
name = 'file_count',
deps = ['app'],
extension = 'h',
)
आसपेक्ट की परिभाषा
file_count_aspect = aspect(
implementation = _file_count_aspect_impl,
attr_aspects = ['deps'],
attrs = {
'extension' : attr.string(values = ['*', 'h', 'cc']),
}
)
इस उदाहरण में दिखाया गया है कि deps
एट्रिब्यूट का इस्तेमाल करके, यह आसपेक्ट रेशियो कैसे लागू होता है.
attrs
किसी आसपेक्ट के लिए एट्रिब्यूट के सेट के बारे में बताता है. सार्वजनिक आसपेक्ट एट्रिब्यूट, पैरामीटर तय करते हैं. ये सिर्फ़ bool
, int
या string
टाइप के हो सकते हैं.
नियम के मुताबिक लागू होने वाले पहलुओं के लिए, int
और string
पैरामीटर में values
की जानकारी होनी चाहिए. इस उदाहरण में extension
नाम का एक पैरामीटर है. इसकी वैल्यू के तौर पर '*
', 'h
' या 'cc
' का इस्तेमाल किया जा सकता है.
नियम के मुताबिक लागू होने वाले एस्पेक्ट के लिए, पैरामीटर की वैल्यू उस नियम से ली जाती है जिसमें एस्पेक्ट का अनुरोध किया गया है. इसके लिए, उस नियम के एट्रिब्यूट का इस्तेमाल किया जाता है जिसका नाम और टाइप एक ही होता है.
(file_count_rule
की परिभाषा देखें).
कमांड-लाइन के पहलुओं के लिए, पैरामीटर की वैल्यू को --aspects_parameters
फ़्लैग का इस्तेमाल करके पास किया जा सकता है. int
और string
पैरामीटर की values
पाबंदी को छोड़ा जा सकता है.
ऐसेट में label
या
label_list
टाइप के निजी एट्रिब्यूट भी हो सकते हैं. निजी लेबल एट्रिब्यूट का इस्तेमाल, उन टूल या लाइब्रेरी पर निर्भरता बताने के लिए किया जा सकता है जो ऐसेट से जनरेट की गई कार्रवाइयों के लिए ज़रूरी हैं. इस उदाहरण में कोई निजी एट्रिब्यूट तय नहीं किया गया है. हालांकि, नीचे दिए गए कोड स्निपेट से पता चलता है कि किसी टूल को किसी पहलू में कैसे पास किया जा सकता है:
...
attrs = {
'_protoc' : attr.label(
default = Label('//tools:protoc'),
executable = True,
cfg = "exec"
)
}
...
आसपेक्ट को लागू करना
FileCountInfo = provider(
fields = {
'count' : 'number of files'
}
)
def _file_count_aspect_impl(target, ctx):
count = 0
# Make sure the rule has a srcs attribute.
if hasattr(ctx.rule.attr, 'srcs'):
# Iterate through the sources counting files
for src in ctx.rule.attr.srcs:
for f in src.files.to_list():
if ctx.attr.extension == '*' or ctx.attr.extension == f.extension:
count = count + 1
# Get the counts from our dependencies.
for dep in ctx.rule.attr.deps:
count = count + dep[FileCountInfo].count
return [FileCountInfo(count = count)]
नियम लागू करने वाले फ़ंक्शन की तरह ही, किसी एस्पेक्ट को लागू करने वाला फ़ंक्शन, उन प्रोवाइडर का स्ट्रक्चर दिखाता है जिन्हें उसकी डिपेंडेंसी ऐक्सेस कर सकती हैं.
इस उदाहरण में, FileCountInfo
को सेवा देने वाली ऐसी कंपनी के तौर पर दिखाया गया है जिसके पास एक फ़ील्ड count
है. fields
एट्रिब्यूट का इस्तेमाल करके, सेवा देने वाली कंपनी के फ़ील्ड को साफ़ तौर पर तय करना सबसे सही तरीका है.
किसी ऐस्पेक्ट ऐप्लिकेशन A(X) के लिए, सेवा देने वाली कंपनियों का सेट, उन कंपनियों का यूनियन होता है जो टारगेट X के लिए नियम लागू करने और ऐस्पेक्ट A को लागू करने से मिलती हैं. नियम लागू करने पर, प्रोवाइडर को बनाया और फ़्रीज़ किया जाता है. ऐसा, ऐस्पेक्ट लागू होने से पहले किया जाता है. साथ ही, ऐस्पेक्ट से इनमें बदलाव नहीं किया जा सकता. अगर टारगेट और उस पर लागू किया गया कोई भी आसपेक्ट रेशियो
OutputGroupInfo
(जो कि मर्ज किया गया है, जब तक कि नियम और आसपेक्ट रेशियो अलग-अलग आउटपुट ग्रुप बताता है) और
InstrumentedFilesInfo
(जो आसपेक्ट रेशियो से लिया गया है) के अपवादों के साथ, तो यह एक गड़बड़ी है. इसका मतलब है कि आसपेक्ट रेशियो (लंबाई-चौड़ाई का अनुपात) को लागू करने पर,
DefaultInfo
कभी भी वापस नहीं आएगा.
पैरामीटर और निजी एट्रिब्यूट, ctx
के एट्रिब्यूट में पास किए जाते हैं. इस उदाहरण में, extension
पैरामीटर के बारे में बताया गया है. इससे तय होता है कि
किन फ़ाइलों की गिनती की जाए.
सेवा देने वाली उन कंपनियों के लिए, जिनके लिए एस्पेक्ट दिखाया जाना है उनके उन एट्रिब्यूट की वैल्यू को बदल दिया जाता है जिनके लिए एस्पेक्ट को लागू किया गया है. ये वैल्यू, attr_aspects
सूची से ली जाती हैं. उदाहरण के लिए, अगर target
X के डिपेंडेंसी में Y और Z हैं, तो A(X) के लिए ctx.rule.attr.deps
[A(Y), A(Z)] होगा.
इस उदाहरण में, ctx.rule.attr.deps
ऐसे टारगेट ऑब्जेक्ट हैं जो उस ओरिजनल टारगेट के 'डिप' पर, पहलू को लागू करने का नतीजा होते हैं जिस पर पहलू लागू किया गया है.
उदाहरण में, एस्पेक्ट, टारगेट की डिपेंडेंसी से FileCountInfo
प्रोवाइडर को ऐक्सेस करता है, ताकि फ़ाइलों की कुल ट्रांज़िशन संख्या इकट्ठा की जा सके.
किसी नियम से एस्पेक्ट को लागू करना
def _file_count_rule_impl(ctx):
for dep in ctx.attr.deps:
print(dep[FileCountInfo].count)
file_count_rule = rule(
implementation = _file_count_rule_impl,
attrs = {
'deps' : attr.label_list(aspects = [file_count_aspect]),
'extension' : attr.string(default = '*'),
},
)
नियम लागू करने से पता चलता है कि ctx.attr.deps
के ज़रिए FileCountInfo
को कैसे ऐक्सेस किया जा सकता है.
नियम की परिभाषा से पता चलता है कि किसी पैरामीटर (extension
) को कैसे तय किया जाता है और उसे डिफ़ॉल्ट वैल्यू (*
) कैसे दी जाती है. ध्यान दें कि आसपेक्ट रेशियो की परिभाषा में पैरामीटर पर लगाई गई पाबंदियों की वजह से, 'cc
', 'h
' या '*
' में से किसी एक के अलावा कोई दूसरी डिफ़ॉल्ट वैल्यू देने पर गड़बड़ी होगी.
टारगेट नियम की मदद से किसी पहलू को लागू करना
load('//:file_count.bzl', 'file_count_rule')
cc_binary(
name = 'app',
...
)
file_count_rule(
name = 'file_count',
deps = ['app'],
extension = 'h',
)
इससे यह पता चलता है कि नियम के ज़रिए, आसपेक्ट में extension
पैरामीटर को कैसे पास किया जाता है. नियम लागू करने के लिए, extension
पैरामीटर की डिफ़ॉल्ट वैल्यू होती है. इसलिए, extension
को वैकल्पिक पैरामीटर माना जाएगा.
file_count
टारगेट बनने के बाद, हमारे ऐस्पेक्ट का आकलन खुद के लिए किया जाएगा. साथ ही, deps
के ज़रिए बार-बार ऐक्सेस किए जा सकने वाले सभी टारगेट का आकलन भी किया जाएगा.