पहलू

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

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

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

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

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

पहलू लागू करने वाले फ़ंक्शन, नियम लागू करने वाले फ़ंक्शन जैसे ही होते हैं. वे 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 के ज़रिए बार-बार ऐक्सेस किया जाएगा.

References