तारीख सेव करें: BazelCon 2023, 24 से 25 अक्टूबर तक Google म्यूनिख में होगा! ज़्यादा जानें

पहलू

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

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

आसपेक्ट के आधार पर, बिल्ड डिपेंडेंसी ग्राफ़ को ज़्यादा जानकारी और कार्रवाइयों के साथ बेहतर बनाया जा सकता है. कुछ सामान्य परिस्थितियां, जिनमें पहलू उपयोगी हो सकते हैं:

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

आसपेक्ट की मूल बातें

BUILD फ़ाइलें किसी प्रोजेक्ट के सोर्स कोड की जानकारी देती हैं: प्रोजेक्ट की कौनसी सोर्स फ़ाइलें, किन आर्टफ़ैक्ट (टारगेट) का इस्तेमाल करके बनाई जानी चाहिए, उन फ़ाइलों के बीच किस तरह की डिपेंडेंसी हैं वगैरह. 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 फ़ाइल डिपेंडेंसी ग्राफ़.

ऊपर से तय किए गए हर टारगेट के लिए, सरफ़ेस (इस मामले में "java_library") के लागू करने के फ़ंक्शन को कॉल करके बेज़ल इस डिपेंडेंसी ग्राफ़ का विश्लेषण करता है. नियम लागू करने वाली कार्रवाइयां, ऐसी कार्रवाइयां जनरेट करती हैं जो आर्टफ़ैक्ट बनाती हैं, जैसे कि .jar फ़ाइलें. साथ ही, उन जगहों की जानकारी और उन आर्टफ़ैक्ट के नाम को कंपनी के उन टारगेट की डिपेंडेंसी पर निर्भर करती हैं.

आसपेक्ट की सुविधा, नियमों की तरह ही होती है. इसमें लागू करने का फ़ंक्शन, कार्रवाइयां जनरेट करता है और डेटा उपलब्ध कराने वाली कंपनी को दिखाता है. हालांकि, उनकी साज़िश, उनके लिए डिपेंडेंसी ग्राफ़ बनाने के तरीके से मिलती है. एक पहलू को लागू करना और उन सभी विशेषताओं की एक सूची होती है जो उसे साथ में लागू करते हैं. "A" नाम वाले एट्रिब्यूट के साथ सबसे पहले दिखाए जाने वाले आसपेक्ट A के बारे में सोचें. इस पहलू को टारगेट X पर लागू किया जा सकता है, जिससे आसपेक्ट ऐप्लिकेशन नोड A(X) जनरेट होता है. इसका इस्तेमाल करने के दौरान, A वर्ण उन सभी टारगेट पर बार-बार लागू किए जाते हैं जिनके बारे में X अपनी "Deps" विशेषता में बताता है.

इसलिए, टारगेट X में A आसपेक्ट को लागू करने का एक ऐक्शन, नीचे दिए गए डायग्राम में दिखाए गए टारगेट के ओरिजनल डिपेंडेंसी ग्राफ़ का "शैडो ग्राफ़" बनाता है:

आसपेक्ट रेशियो वाला ग्राफ़ बनाना

दूसरा डायग्राम. अलग-अलग पहलुओं की मदद से ग्राफ़ बनाएं.

शैडो सेट किए गए सिर्फ़ वे किनारे होते हैं जो प्रॉपपेशन सेट में मौजूद विशेषताओं को बताते हैं. इसलिए, इस उदाहरण में runtime_deps को शैडो नहीं किया गया है. इसके बाद, एक फ़ंक्शन लागू करने की प्रक्रिया को शैडो ग्राफ़ के सभी नोड पर उसी तरह लागू किया जाता है जिस तरह मूल ग्राफ़ के नोड पर नियम लागू किया जाता है.

सरल उदाहरण

इस उदाहरण में, किसी नियम और उसकी deps डिपेंडेंसी के लिए, सोर्स फ़ाइलों को बार-बार प्रिंट करने का तरीका बताया गया है. इसमें एक आसपेक्ट रेशियो (चौड़ाई-ऊंचाई का अनुपात), लागू करने का तरीका, और Bazel कमांड लाइन से आसपेक्ट को शुरू करने का तरीका बताया गया है.

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 किसी पहलू के लिए एट्रिब्यूट के सेट के बारे में बताता है. सार्वजनिक आसपेक्ट एट्रिब्यूट stringटाइप के होते हैं और उन्हें पैरामीटर कहा जाता है. पैरामीटर मेंvalues एट्रिब्यूट शामिल होना चाहिए. इस उदाहरण में extension नाम का एक पैरामीटर है, जिसमें वैल्यू के रूप में '*', 'h' या 'cc' हो सकते हैं.

आसपेक्ट रेशियो (चौड़ाई-ऊंचाई का अनुपात) से जुड़ी वैल्यू, स्ट्रिंग एट्रिब्यूट से ली जाती हैं. इसमें वही डाइमेंशन होता है जो आसपेक्ट का अनुरोध करता है (file_count_rule की परिभाषा देखें). पैरामीटर वाले आसपेक्ट का इस्तेमाल कमांड लाइन के ज़रिए नहीं किया जा सकता, क्योंकि पैरामीटर तय करने के लिए कोई सिंटैक्स नहीं है.

पहलुओं में 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 सूची से बदला जाता है जिनके साथ उस आसपेक्ट को फैलाया जाता है. इन एट्रिब्यूट की वैल्यू, आसपेक्ट रेशियो वाले किसी ऐप्लिकेशन के नतीजों से बदल दी जाती हैं. उदाहरण के लिए, अगर टारगेट 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 के ज़रिए बार-बार ऐक्सेस किए जाने वाले सभी टारगेट के लिए किया जाता है.

रेफ़रंस