बाहरी डिपेंडेंसी के साथ काम करना

Bazel, दूसरे प्रोजेक्ट के टारगेट पर निर्भर हो सकता है. इन दूसरे प्रोजेक्ट की डिपेंडेंसी को एक्सटर्नल डिपेंडेंसी कहा जाता है.

वर्कस्पेस डायरेक्ट्री में मौजूद WORKSPACE फ़ाइल (या WORKSPACE.bazel फ़ाइल), Bazel को यह बताती है कि दूसरे प्रोजेक्ट के सोर्स कैसे हासिल किए जाएं. इन दूसरे प्रोजेक्ट में, अपने टारगेट के साथ एक या उससे ज़्यादा BUILD फ़ाइलें हो सकती हैं. मुख्य प्रोजेक्ट में मौजूद BUILD फ़ाइलें, WORKSPACE फ़ाइल से उनके नाम का इस्तेमाल करके, इन एक्सटर्नल टारगेट पर निर्भर हो सकती हैं.

उदाहरण के लिए, मान लें कि किसी सिस्टम पर दो प्रोजेक्ट मौजूद हैं:

/
  home/
    user/
      project1/
        WORKSPACE
        BUILD
        srcs/
          ...
      project2/
        WORKSPACE
        BUILD
        my-libs/

अगर project1 को /home/user/project2/BUILD में तय किए गए टारगेट :foo पर निर्भर होना है, तो वह यह तय कर सकता है कि project2 नाम का डेटा स्टोर करने की जगह, /home/user/project2 पर मौजूद है. इसके बाद, /home/user/project1/BUILD में मौजूद टारगेट, @project2//:foo पर निर्भर हो सकते हैं.

WORKSPACE फ़ाइल की मदद से, उपयोगकर्ता फ़ाइल सिस्टम के दूसरे हिस्सों से टारगेट पर निर्भर हो सकते हैं. साथ ही, इंटरनेट से डाउनलोड किए गए टारगेट पर भी निर्भर हो सकते हैं. यह BUILD फ़ाइलों के सिंटैक्स का ही इस्तेमाल करती है. हालांकि, इसमें डेटा स्टोर करने की जगह के नियम नाम के नियमों का एक अलग सेट इस्तेमाल किया जाता है. इन्हें कभी-कभी वर्कस्पेस के नियम भी कहा जाता है. Bazel में, डेटा स्टोर करने की जगह के कुछ बिल्ट-इन नियम और Starlark के एम्बेड किए गए डेटा स्टोर करने की जगह के नियमों का एक सेट शामिल है. उपयोगकर्ता, ज़्यादा जटिल व्यवहार पाने के लिए, डेटा स्टोर करने की जगह के कस्टम नियम भी लिख सकते हैं.

एक्सटर्नल डिपेंडेंसी के इस्तेमाल किए जा सकने वाले टाइप

एक्सटर्नल डिपेंडेंसी के कुछ बुनियादी टाइप इस्तेमाल किए जा सकते हैं:

Bazel के दूसरे प्रोजेक्ट पर डिपेंडेंसी

अगर आपको Bazel के दूसरे प्रोजेक्ट के टारगेट इस्तेमाल करने हैं, तो आप इस्तेमाल कर सकते हैं local_repository, git_repository या http_archive उसे लोकल फ़ाइल सिस्टम से सिमलंक करने, गिट डेटा स्टोर करने की जगह का रेफ़रंस देने या उसे डाउनलोड करने के लिए (क्रमशः).

उदाहरण के लिए, मान लें कि आप my-project/ नाम के किसी प्रोजेक्ट पर काम कर रहे हैं और आपको अपने सहकर्मी के coworkers-project/ नाम के प्रोजेक्ट के टारगेट पर निर्भर होना है. दोनों प्रोजेक्ट Bazel का इस्तेमाल करते हैं. इसलिए, अपने सहकर्मी के प्रोजेक्ट को एक्सटर्नल डिपेंडेंसी के तौर पर जोड़ा जा सकता है. इसके बाद, अपने सहकर्मी के तय किए गए किसी भी टारगेट को, अपनी BUILD फ़ाइलों से इस्तेमाल किया जा सकता है. my_project/WORKSPACE में यह जोड़ा जाएगा:

local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
)

अगर आपके सहकर्मी के पास //foo:bar नाम का कोई टारगेट है, तो आपका प्रोजेक्ट उसे @coworkers_project//foo:bar के तौर पर रेफ़र कर सकता है. एक्सटर्नल प्रोजेक्ट के नाम, वर्कस्पेस के मान्य नाम होने चाहिए.

Bazel के अलावा दूसरे प्रोजेक्ट पर डिपेंडेंसी

new_ से शुरू होने वाले नियम, जैसे कि new_local_repository, आपको उन प्रोजेक्ट से टारगेट बनाने की अनुमति देते हैं जो Bazel का इस्तेमाल नहीं करते.

उदाहरण के लिए, मान लें कि आप my-project/ नाम के किसी प्रोजेक्ट पर काम कर रहे हैं और आपको अपने सहकर्मी के coworkers-project/ नाम के प्रोजेक्ट पर निर्भर होना है. आपका सहकर्मी, प्रोजेक्ट बनाने के लिए make का इस्तेमाल करता है. हालांकि, आपको उससे जनरेट होने वाली .so फ़ाइलों में से किसी एक पर निर्भर होना है. ऐसा करने के लिए, my_project/WORKSPACE में यह जोड़ें:

new_local_repository(
    name = "coworkers_project",
    path = "/path/to/coworkers-project",
    build_file = "coworker.BUILD",
)

build_file , मौजूदा प्रोजेक्ट पर ओवरले करने के लिए, BUILD फ़ाइल तय करता है. जैसे:

cc_library(
    name = "some-lib",
    srcs = glob(["**"]),
    visibility = ["//visibility:public"],
)

इसके बाद, अपने प्रोजेक्ट की BUILD फ़ाइलों से @coworkers_project//:some-lib पर निर्भर हुआ जा सकता है.

एक्सटर्नल पैकेज पर डिपेंडेंसी

Maven के आर्टफ़ैक्ट और डेटा स्टोर करने की जगहें

Maven के डेटा स्टोर करने की जगहों से आर्टफ़ैक्ट डाउनलोड करने और उन्हें Java डिपेंडेंसी के तौर पर उपलब्ध कराने के लिए, rules_jvm_external के नियम सेट का इस्तेमाल करें.

डिपेंडेंसी फ़ेच करना

डिफ़ॉल्ट रूप से, एक्सटर्नल डिपेंडेंसी को bazel build के दौरान, ज़रूरत के हिसाब से फ़ेच किया जाता है. अगर आपको टारगेट के किसी खास सेट के लिए ज़रूरी डिपेंडेंसी को पहले से फ़ेच करना है, तो bazel fetch का इस्तेमाल करें. सभी एक्सटर्नल डिपेंडेंसी को बिना शर्त फ़ेच करने के लिए, bazel sync का इस्तेमाल करें. फ़ेच किए गए डेटा स्टोर करने की जगहों को आउटपुट बेस में सेव किया जाता है. इसलिए, फ़ेचिंग हर वर्कस्पेस के लिए होती है.

डिपेंडेंसी को शैडो करना

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

myproject/WORKSPACE

workspace(name = "myproject")

local_repository(
    name = "A",
    path = "../A",
)
local_repository(
    name = "B",
    path = "../B",
)

A/WORKSPACE

workspace(name = "A")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "...",
)

B/WORKSPACE

workspace(name = "B")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)

डिपेंडेंसी A और B, दोनों testrunner पर निर्भर हैं. हालांकि, वे testrunner के अलग-अलग वर्शन पर निर्भर हैं. myproject में इन टेस्ट रनर के साथ-साथ काम न करने की कोई वजह नहीं है. हालांकि, ये एक-दूसरे से टकराएंगे, क्योंकि इनके नाम एक जैसे हैं. दोनों डिपेंडेंसी तय करने के लिए, myproject/WORKSPACE को अपडेट करें:

workspace(name = "myproject")

load("@bazel_tools//tools/build_defs/repo:http.bzl", "http_archive")
http_archive(
    name = "testrunner-v1",
    urls = ["https://github.com/testrunner/v1.zip"],
    sha256 = "..."
)
http_archive(
    name = "testrunner-v2",
    urls = ["https://github.com/testrunner/v2.zip"],
    sha256 = "..."
)
local_repository(
    name = "A",
    path = "../A",
    repo_mapping = {"@testrunner" : "@testrunner-v1"}
)
local_repository(
    name = "B",
    path = "../B",
    repo_mapping = {"@testrunner" : "@testrunner-v2"}
)

इस तरीके का इस्तेमाल, डायमंड को जोड़ने के लिए भी किया जा सकता है. उदाहरण के लिए, अगर A और B की डिपेंडेंसी एक ही है, लेकिन उन्हें अलग-अलग नामों से कॉल किया जाता है, तो उन डिपेंडेंसी को myproject/WORKSPACE में जोड़ा जा सकता है.

कमांड लाइन से डेटा स्टोर करने की जगहों को बदलना

कमांड लाइन से, तय की गई किसी डेटा स्टोर करने की जगह को लोकल डेटा स्टोर करने की जगह से बदलने के लिए, use the --override_repository फ़्लैग का इस्तेमाल करें. इस फ़्लैग का इस्तेमाल करने से, सोर्स कोड में बदलाव किए बिना, एक्सटर्नल डेटा स्टोर करने की जगहों का कॉन्टेंट बदल जाता है.

उदाहरण के लिए, लोकल डायरेक्ट्री /path/to/local/foo से @foo को बदलने के लिए, --override_repository=foo=/path/to/local/foo फ़्लैग पास करें.

इसके कुछ इस्तेमाल के उदाहरण यहां दिए गए हैं:

  • डीबग करने से जुड़ी समस्याएं. उदाहरण के लिए, http_archive डेटा स्टोर करने की जगह को किसी लोकल डायरेक्ट्री से बदला जा सकता है. इससे, उसमें आसानी से बदलाव किए जा सकते हैं.
  • वेंडरिंग. अगर आप ऐसे एनवायरमेंट में हैं जहां नेटवर्क कॉल नहीं किए जा सकते, तो नेटवर्क पर आधारित डेटा स्टोर करने की जगह के नियमों को बदलकर, उन्हें लोकल डायरेक्ट्री पर पॉइंट करें.

प्रॉक्सी का इस्तेमाल करना

Bazel, HTTPS_PROXY और HTTP_PROXY एनवायरमेंट वैरिएबल से प्रॉक्सी पते लेगा. साथ ही, इनका इस्तेमाल करके, एचटीटीपी/एचटीटीपीएस फ़ाइलें डाउनलोड करेगा. हालांकि, ऐसा तब होगा, जब ये वैरिएबल तय किए गए हों.

IPv6 के लिए सहायता

सिर्फ़ IPv6 वाले कंप्यूटरों पर, Bazel बिना किसी बदलाव के डिपेंडेंसी डाउनलोड कर पाएगा. हालांकि, डुअल-स्टैक IPv4/IPv6 वाले कंप्यूटरों पर, Bazel, Java के जैसे ही काम करता है. इसका मतलब है कि अगर IPv4 चालू है, तो IPv4 को प्राथमिकता दी जाती है. कुछ स्थितियों में, जैसे कि जब IPv4 नेटवर्क, एक्सटर्नल पतों को हल/उन तक नहीं पहुंच पाता, तो Network unreachable अपवाद हो सकते हैं और बिल्ड फ़ेल हो सकते हैं. ऐसे मामलों में, Bazel के व्यवहार को बदलकर, IPv6 को प्राथमिकता दी जा सकती है. इसके लिए, java.net.preferIPv6Addresses=true सिस्टम प्रॉपर्टी का इस्तेमाल करें. खास तौर पर, इस बारे में जानकारी मिलती है:

  • स्टार्टअप विकल्प --host_jvm_args=-Djava.net.preferIPv6Addresses=true का इस्तेमाल करें, उदाहरण के लिए, अपनी .bazelrc फ़ाइल में यह लाइन जोड़ें:

    startup --host_jvm_args=-Djava.net.preferIPv6Addresses=true

  • अगर Java के ऐसे बिल्ड टारगेट चलाए जा रहे हैं जिन्हें इंटरनेट से भी कनेक्ट करना है (कभी-कभी इंटिग्रेशन टेस्ट के लिए इसकी ज़रूरत होती है), तो --jvmopt=-Djava.net.preferIPv6Addresses=true टूल फ़्लैग का भी इस्तेमाल करें. उदाहरण के लिए, अपनी .bazelrc फ़ाइल में यह लाइन जोड़ें:

    build --jvmopt=-Djava.net.preferIPv6Addresses

  • अगर rules_jvm_external का इस्तेमाल किया जा रहा है, तो डिपेंडेंसी वर्शन रिज़ॉल्यूशन के लिए, -Djava.net.preferIPv6Addresses=true भी जोड़ें. इससे, COURSIER_OPTS एनवायरमेंट वैरिएबल के लिए Coursier के JVM विकल्प मिलेंगे

ट्रांज़िटिव डिपेंडेंसी

Bazel, सिर्फ़ आपकी WORKSPACE फ़ाइल में मौजूद डिपेंडेंसी को पढ़ता है. अगर आपका प्रोजेक्ट (A), किसी दूसरे प्रोजेक्ट (B) पर निर्भर है और उसकी WORKSPACE फ़ाइल में, तीसरे प्रोजेक्ट (C) पर डिपेंडेंसी तय की गई है, तो आपको अपने प्रोजेक्ट की WORKSPACE फ़ाइल में B और C, दोनों को जोड़ना होगा. इस ज़रूरी शर्त की वजह से, WORKSPACE फ़ाइल का साइज़ बढ़ सकता है. हालांकि, इससे इस बात की संभावना कम हो जाती है कि एक लाइब्रेरी में C का वर्शन 1.0 और दूसरी लाइब्रेरी में C का वर्शन 2.0 शामिल हो.

एक्सटर्नल डिपेंडेंसी की कैश मेमोरी

डिफ़ॉल्ट रूप से, Bazel, एक्सटर्नल डिपेंडेंसी को सिर्फ़ तब फिर से डाउनलोड करेगा, जब उनकी परिभाषा में बदलाव होगा. Bazel, परिभाषा में रेफ़रंस दी गई फ़ाइलों (जैसे कि पैच या BUILD फ़ाइलें) में किए गए बदलावों को भी ध्यान में रखता है.

फिर से डाउनलोड करने के लिए, bazel sync का इस्तेमाल करें.

लेआउट

एक्सटर्नल डिपेंडेंसी, आउटपुट बेस में मौजूद सबडायरेक्ट्री external के तहत किसी डायरेक्ट्री में डाउनलोड की जाती हैं. लोकल डेटा स्टोर करने की जगह के मामले में, नई डायरेक्ट्री बनाने के बजाय, वहां एक सिमलंक बनाया जाता है. external डायरेक्ट्री देखने के लिए, यह कमांड चलाएं:

ls $(bazel info output_base)/external

ध्यान दें कि bazel clean चलाने से, एक्सटर्नल डायरेक्ट्री असल में नहीं मिटेगी. सभी एक्सटर्नल आर्टफ़ैक्ट हटाने के लिए, bazel clean --expunge का इस्तेमाल करें.

ऑफ़लाइन बिल्ड

कभी-कभी ऑफ़लाइन तरीके से बिल्ड चलाना ज़रूरी होता है या ऐसा करना सही होता है. सामान्य इस्तेमाल के उदाहरणों के लिए, जैसे कि हवाई जहाज़ में यात्रा करते समय, prefetching काफ़ी हो सकता है. इसके अलावा, --nofetch विकल्प का इस्तेमाल करके, बिल्ड के दौरान, आगे के डेटा स्टोर करने की जगहों को फ़ेच करने की सुविधा बंद की जा सकती है.

पूरी तरह ऑफ़लाइन बिल्ड के लिए, जहां ज़रूरी फ़ाइलें Bazel के अलावा किसी दूसरी इकाई से उपलब्ध कराई जानी हैं, Bazel, --distdir विकल्प के साथ काम करता है. जब भी डेटा स्टोर करने की जगह का कोई नियम, Bazel से फ़ाइल फ़ेच करने के लिए ctx.download या ctx.download_and_extract के ज़रिए कहता है और ज़रूरी फ़ाइल का हैश सम उपलब्ध कराता है, तो Bazel, उस विकल्प से तय की गई डायरेक्ट्री में, पहले दिए गए यूआरएल के बेसनेम से मेल खाने वाली फ़ाइल को सबसे पहले देखेगा. अगर हैश मैच होता है, तो वह लोकल कॉपी का इस्तेमाल करेगा.

Bazel, डिस्ट्रिब्यूशन आर्टफ़ैक्ट से ऑफ़लाइन बूटस्ट्रैप करने के लिए, इस तकनीक का इस्तेमाल करता है. यह, ज़रूरी सभी एक्सटर्नल डिपेंडेंसी को, इंटरनल distdir_tar में इकट्ठा करके ऐसा करता है.

हालांकि, Bazel, डेटा स्टोर करने की जगह के नियमों में, किसी भी कमांड को एक्ज़ीक्यूट करने की अनुमति देता है. भले ही, उसे यह पता न हो कि वे नेटवर्क को कॉल करते हैं या नहीं. इसलिए, Bazel के पास, बिल्ड को पूरी तरह ऑफ़लाइन करने की सुविधा नहीं है. इसलिए, यह जांचने के लिए कि कोई बिल्ड ऑफ़लाइन सही तरीके से काम करता है या नहीं, नेटवर्क को एक्सटर्नल तरीके से ब्लॉक करना ज़रूरी है. Bazel, अपने बूटस्ट्रैप टेस्ट में ऐसा ही करता है.

सबसे सही तरीके

डेटा स्टोर करने की जगह के नियम

डेटा स्टोर करने की जगह के किसी नियम को आम तौर पर, इन कामों के लिए ज़िम्मेदार होना चाहिए:

  • सिस्टम सेटिंग का पता लगाना और उन्हें फ़ाइलों में लिखना.
  • सिस्टम में कहीं और मौजूद संसाधन ढूंढना.
  • यूआरएल से संसाधन डाउनलोड करना.
  • एक्सटर्नल डेटा स्टोर करने की जगह की डायरेक्ट्री में, BUILD फ़ाइलें जनरेट करना या उन्हें सिमलंक करना.

जहां तक हो सके, repository_ctx.execute का इस्तेमाल न करें. उदाहरण के लिए, Bazel के अलावा C++ की किसी ऐसी लाइब्रेरी का इस्तेमाल करते समय जिसे Make का इस्तेमाल करके बनाया गया है, ctx.execute(["make"])चलाने के बजाय, repository_ctx.download()का इस्तेमाल करना बेहतर है. इसके बाद, BUILD फ़ाइल लिखें, जो उसे बनाती है.

git_repository और new_git_repository के बजाय, http_archive का इस्तेमाल करें. इसकी वजहें यहां दी गई हैं:

bind() का इस्तेमाल न करें. इसकी समस्याओं और विकल्पों के बारे में ज़्यादा जानने के लिए, "bind को हटाने के बारे में विचार करना" देखें.