पहलू

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

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

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

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

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

BUILD फ़ाइलें किसी प्रोजेक्ट के सोर्स कोड की जानकारी देती हैं: इनमें से कौनसी सोर्स फ़ाइलें प्रोजेक्ट का हिस्सा हैं, उन फ़ाइलों से कौनसे आर्टफ़ैक्ट (टारगेट) बनाए जाने चाहिए, उन फ़ाइलों के बीच क्या डिपेंडेंसी है वगैरह. Baze इस जानकारी का इस्तेमाल बिल्ड करने के लिए करता है. जैसे, यह आर्टफ़ैक्ट बनाने के लिए ज़रूरी कार्रवाइयों के सेट का पता लगाता है. जैसे, कंपाइलर या लिंकर को रन करना और उन कार्रवाइयों को लागू करना. Baज़ल, टारगेट के बीच एक डिपेंडेंसी ग्राफ़ बनाकर और उन कार्रवाइयों को इकट्ठा करने के लिए इस ग्राफ़ पर जाकर ऐसा करता है.

यह 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 []

'आसपेक्ट लागू करने' के फ़ंक्शन, नियम को लागू करने वाले फ़ंक्शन की तरह ही होते हैं. वे providers लौटाते हैं, कार्रवाइयां जनरेट कर सकते हैं और दो तर्क ले सकते हैं:

  • 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 के ज़रिए बार-बार ऐक्सेस किए जा सकने वाले सभी टारगेट का आकलन किया जाएगा.

रेफ़रंस