पहलू

किसी समस्या की शिकायत करें सोर्स देखें Nightly · 7.4 . 7.3 · 7.2 · 7.1 · 7.0 · 6.5

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

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

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

आसपेक्ट से जुड़ी बुनियादी बातें

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 फ़ाइल का डिपेंडेंसी ग्राफ़.

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

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

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

रेफ़रंस