पहलू

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

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

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